From patchwork Thu Sep 19 10:52:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152133 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DBAD514DB for ; Thu, 19 Sep 2019 10:52:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 93FE9217D6 for ; Thu, 19 Sep 2019 10:52:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="VX/25fFH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389406AbfISKwm (ORCPT ); Thu, 19 Sep 2019 06:52:42 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387772AbfISKwl (ORCPT ); Thu, 19 Sep 2019 06:52:41 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=c3LWy5xXS7zlj+bXgEdaiL+rhA9t9FX3u32h/GomXWbCZllyIMIjdJnBYa9w57bB6ITpu9oyWD3p+Mnlh8x0q8p1Nm9blY+6dSpf/NTvH30apumeHSuAE7g1ByFFwAWewJFup/sVYHgfERbWMNcsb62ss6ttP14cKucn4m8uS1o86Tq/ItXMA/0OnSDiTKZbDDUEbgmefVgT7VwoiCwFe8dIG/7BDpU0aBl6MDdmAfHkAqnCxYaniQlfIDEtiy2WA+M8g6ffkzWNhu/tJBS32K/AN6YbyOBOT+NZSsmLnZXS0aLHtzi3qamWEa8ZtgdgrrQex9EPZl3fl5YcC+JMfw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=bmuP0wVKTeBAmbWDwuDg/J4UqPpaRbYwbeQahSgCrK0=; b=M3VOeR3IyUjWbjCwOnKat2YFOITJs0t4bJvCY5tNN/cbITFJpTgZ8RM6Q1p89GxzHegKonBHyN683OlXT+XZSd/giOTU6jlTpkQ6IZbpzUpVX6OOVJ09+lbdSsJlLN82dUm8gWWfdlGvncatpsqMujyc9fT3ObtK/h6BpTqYZMSXUe3viYtIBGQP5WHRAOHvdiYiVRfrymhDR1NEs4NX3DZIdtt6hCwXaRQUBS/DXrjc2ieQM3gifcDYawh0xl1oMNpjuoq4Z2jfwnELu434LVEYnbz8sykaXCyIfAc4wvYzOCiIMp1Tm/cm+zyzSkYmwQL2I9SskV4JOHYgzj+t0w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=bmuP0wVKTeBAmbWDwuDg/J4UqPpaRbYwbeQahSgCrK0=; b=VX/25fFHhxf9Vk9lN15v5nLQfzhf0wkq0K184ShxT4bNuxi8AAv4VI1pAuuHXTs/7/cYd4tJY2hPjVHD0JBA0wHAbjiNiUnaed0CEXZzcETqHwpAAc8DlMfeOzfoqK1o/ImSRQmD/znDv2gn8ApSoYYYnCNcKMi88bH8rLVibuQ= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:35 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:35 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 01/20] staging: wfx: add infrastructure for new driver Thread-Topic: [PATCH 01/20] staging: wfx: add infrastructure for new driver Thread-Index: AQHVbthXG8E30KBeIk2e2gDto0pOYA== Date: Thu, 19 Sep 2019 10:52:34 +0000 Message-ID: <20190919105153.15285-2-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: ef6ae4ec-8ab0-410f-4ba8-08d73cef7acd x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:9508; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(966005)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(6306002)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: UPjPLOmTTc28LNW1hqYVfq9Y0I0RLPukv95LG7mqXZMjQ+5cDEH2ymMtdU54IPBr9t09a5HIJbb6aVtBoiv8tFiRNQacF1gSYiRvoPXMfo4h3mGSN6pDEMmGCYkYur1DCFDZPhllydr1hKEEIOzhGB3+4n7/5rKFHhBrz+AUFv9jmeQKv8VNtcAiL/nvS7I45YZZXRyXeGxhnVnFSGdpoXc7PvrgKaU87hQ1scLn30fTTUnPSXhFn57KNvQk29qUxloCf48BQs/QO6gvyODFEUVb60P+3yT97YkZYL38Rj+PsPHB3KlrL+s755LVq14f4w0dCkvpoagjQyETJJlYH5QQcNI701MLBVKgrmm00VfJkPcry416pOw3Fkg8hTl/RgZ2NuAvf6UIzOdAQnyxRrkpwuvYPHAgjcC+ZMiowVI= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: ef6ae4ec-8ab0-410f-4ba8-08d73cef7acd X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:34.5442 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: ty4hZdEYkPxq99GzKsA1uOSw/gzl64+Slh7t9q4qlfCYnwoj8O2nv7FLCkaCjupYaJu0ltkIf2Sv0pMQoSywSw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Instantiate build infrastructure WFx driver. This driver provides support for Wifi chipset Silicon Labs WF200 and further: https://www.silabs.com/documents/public/data-sheets/wf200-datasheet.pdf This chip support SPI and SDIO bus. SDIO interface has two particularities: 1. Some parameters may be useful for end user (I will talk about gpio_wakeup later). 2. The SDIO VID and PID of WF200 are 0000:0001 which are too much generic to rely on. So, current code checks VID/PID and looks for a node in DT (since WF200 targets embedded platforms, I don't think it is a problem to rely on DT). DT can also be used to define to parameters for driver. Currently, if no node is found, a warning is emitted, but it could be changed in error. Signed-off-by: Jérôme Pouiller --- MAINTAINERS | 5 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + .../bindings/net/wireless/siliabs,wfx.txt | 97 +++++++++++++++++++ drivers/staging/wfx/Kconfig | 7 ++ drivers/staging/wfx/Makefile | 8 ++ drivers/staging/wfx/bus.h | 17 ++++ drivers/staging/wfx/bus_sdio.c | 70 +++++++++++++ drivers/staging/wfx/bus_spi.c | 53 ++++++++++ drivers/staging/wfx/main.c | 47 +++++++++ drivers/staging/wfx/wfx_version.h | 3 + 11 files changed, 310 insertions(+) create mode 100644 drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt create mode 100644 drivers/staging/wfx/Kconfig create mode 100644 drivers/staging/wfx/Makefile create mode 100644 drivers/staging/wfx/bus.h create mode 100644 drivers/staging/wfx/bus_sdio.c create mode 100644 drivers/staging/wfx/bus_spi.c create mode 100644 drivers/staging/wfx/main.c create mode 100644 drivers/staging/wfx/wfx_version.h diff --git a/MAINTAINERS b/MAINTAINERS index b2326dece28e..0ad6fbde3ac9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14755,6 +14755,11 @@ S: Maintained F: drivers/input/touchscreen/silead.c F: drivers/platform/x86/touchscreen_dmi.c +SILICON LABS WIRELESS DRIVERS (for WFxxx series) +M: Jérôme Pouiller +S: Supported +F: drivers/staging/wfx/ + SILICON MOTION SM712 FRAME BUFFER DRIVER M: Sudip Mukherjee M: Teddy Wang diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6f1fa4c849a1..a490141a0e88 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -125,4 +125,6 @@ source "drivers/staging/exfat/Kconfig" source "drivers/staging/qlge/Kconfig" +source "drivers/staging/wfx/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a90f9b308c8d..4cb548a0ff87 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_WUSB) += wusbcore/ obj-$(CONFIG_EXFAT_FS) += exfat/ obj-$(CONFIG_QLGE) += qlge/ +obj-$(CONFIG_WFX) += wfx/ diff --git a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt new file mode 100644 index 000000000000..15965c9b4180 --- /dev/null +++ b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt @@ -0,0 +1,97 @@ +The WFxxx chip series can be connected via SPI or via SDIO. + +SPI +--- + +You have to declare the WFxxx chip in your device tree. + +Required properties: + - compatible: Should be "silabs,wfx-spi" + - reg: Chip select address of device + - spi-max-frequency: Maximum SPI clocking speed of device in Hz + - interrupts-extended: Should contain interrupt line (interrupt-parent + + interrupt can also been used). Trigger should be `IRQ_TYPE_EDGE_RISING`. + +Optional properties: + - reset-gpios: phandle of gpio that will be used to reset chip during probe. + Without this property, you may encounter issues with warm boot. + +Please consult Documentation/devicetree/bindings/spi/spi-bus.txt for optional +SPI connection related properties, + +Example: + +&spi1 { + wfx { + compatible = "silabs,wfx-spi"; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_irq &wfx_gpios>; + interrupts-extended = <&gpio 16 IRQ_TYPE_EDGE_RISING>; + wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + reg = <0>; + spi-max-frequency = <42000000>; + }; +}; + + +SDIO +---- + +The driver is able to detect a WFxxx chip on SDIO bus by matching its Vendor ID +and Product ID. However, driver will only provide limited features in this +case. Thus declaring WFxxx chip in device tree is strongly recommended (and may +become mandatory in the future). + +Required properties: + - compatible: Should be "silabs,wfx-sdio" + - reg: Should be 1 + +In addition, it is recommended to declare a mmc-pwrseq on SDIO host above WFx. +Without it, you may encounter issues with warm boot. mmc-pwrseq should be +compatible with mmc-pwrseq-simple. Please consult +Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt for more +information. + +Example: + +/ { + wfx_pwrseq: wfx_pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_reset>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + }; +}; + +&mmc1 { + mmc-pwrseq = <&wfx_pwrseq>; + #address-size = <1>; + #size = <0>; + + mmc@1 { + compatible = "silabs,wfx-sdio"; + reg = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&wfx_wakeup>; + wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; + }; +}; + +Note that #address-size and #size shoud already be defined in node mmc1, but it +is rarely the case. + +Common properties +----------------- + +Some properties are recognized either by SPI and SDIO versions: + - wakeup-gpios: phandle of gpio that will be used to wake-up chip. Without + this property, driver will disable most of power saving features. + - config-file: Use an alternative file as PDS. Default is `wf200.pds`. Only + necessary for development/debug purpose. + - slk_key: String representing hexdecimal value of secure link key to use. + Must contains 64 hexadecimal digits. Not supported in current version. + +WFx driver also supports `mac-address` and `local-mac-address` as described in +Documentation/devicetree/binding/net/ethernet.txt + diff --git a/drivers/staging/wfx/Kconfig b/drivers/staging/wfx/Kconfig new file mode 100644 index 000000000000..9b8a1c7a9e90 --- /dev/null +++ b/drivers/staging/wfx/Kconfig @@ -0,0 +1,7 @@ +config WFX + tristate "Silicon Labs wireless chips WF200 and further" + depends on MAC80211 + depends on (SPI || MMC) + help + This is a driver for Silicons Labs WFxxx series (WF200 and further) + chipsets. This chip can be found on SPI or SDIO buses. diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile new file mode 100644 index 000000000000..74939a5a0a1c --- /dev/null +++ b/drivers/staging/wfx/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +wfx-y := \ + main.o +wfx-$(CONFIG_SPI) += bus_spi.o +wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o + +obj-$(CONFIG_WFX) += wfx.o diff --git a/drivers/staging/wfx/bus.h b/drivers/staging/wfx/bus.h new file mode 100644 index 000000000000..8ce871a8a9ff --- /dev/null +++ b/drivers/staging/wfx/bus.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common bus abstraction layer. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_BUS_H +#define WFX_BUS_H + +#include +#include + +extern struct sdio_driver wfx_sdio_driver; +extern struct spi_driver wfx_spi_driver; + +#endif diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c new file mode 100644 index 000000000000..4b26c994f43c --- /dev/null +++ b/drivers/staging/wfx/bus_sdio.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SDIO interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include +#include + +#include "bus.h" + +static const struct of_device_id wfx_sdio_of_match[]; +static int wfx_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct device_node *np = func->dev.of_node; + + if (func->num != 1) { + dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n", func->num); + return -ENODEV; + } + + if (np) { + if (!of_match_node(wfx_sdio_of_match, np)) { + dev_warn(&func->dev, "no compatible device found in DT\n"); + return -ENODEV; + } + } else { + dev_warn(&func->dev, "device is not declared in DT, features will be limited\n"); + // FIXME: ignore VID/PID and only rely on device tree + // return -ENODEV; + } + return -EIO; // FIXME: not yet supported +} + +static void wfx_sdio_remove(struct sdio_func *func) +{ +} + +#define SDIO_VENDOR_ID_SILABS 0x0000 +#define SDIO_DEVICE_ID_SILABS_WF200 0x1000 +static const struct sdio_device_id wfx_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_SILABS, SDIO_DEVICE_ID_SILABS_WF200) }, + // FIXME: ignore VID/PID and only rely on device tree + // { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, + { }, +}; +MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); + +#ifdef CONFIG_OF +static const struct of_device_id wfx_sdio_of_match[] = { + { .compatible = "silabs,wfx-sdio" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); +#endif + +struct sdio_driver wfx_sdio_driver = { + .name = "wfx-sdio", + .id_table = wfx_sdio_ids, + .probe = wfx_sdio_probe, + .remove = wfx_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wfx_sdio_of_match), + } +}; diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c new file mode 100644 index 000000000000..574b60f513e9 --- /dev/null +++ b/drivers/staging/wfx/bus_spi.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2011, Sagrad Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include + +#include "bus.h" + +static int wfx_spi_probe(struct spi_device *func) +{ + return -EIO; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int wfx_spi_disconnect(struct spi_device *func) +{ + return 0; +} + +/* + * For dynamic driver binding, kernel does not use OF to match driver. It only + * use modalias and modalias is a copy of 'compatible' DT node with vendor + * stripped. + */ +static const struct spi_device_id wfx_spi_id[] = { + { "wfx-spi", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, wfx_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id wfx_spi_of_match[] = { + { .compatible = "silabs,wfx-spi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wfx_spi_of_match); +#endif + +struct spi_driver wfx_spi_driver = { + .driver = { + .name = "wfx-spi", + .of_match_table = of_match_ptr(wfx_spi_of_match), + }, + .id_table = wfx_spi_id, + .probe = wfx_spi_probe, + .remove = wfx_spi_disconnect, +}; diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c new file mode 100644 index 000000000000..cd69f955f531 --- /dev/null +++ b/drivers/staging/wfx/main.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Device probe and register. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2008, Johannes Berg + * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2004-2006 Jean-Baptiste Note , et al. + */ +#include +#include +#include +#include + +#include "bus.h" +#include "wfx_version.h" + +MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); +MODULE_AUTHOR("Jérôme Pouiller "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(WFX_LABEL); + +static int __init wfx_core_init(void) +{ + int ret = 0; + + pr_info("wfx: Silicon Labs " WFX_LABEL "\n"); + + if (IS_ENABLED(CONFIG_SPI)) + ret = spi_register_driver(&wfx_spi_driver); + if (IS_ENABLED(CONFIG_MMC) && !ret) + ret = sdio_register_driver(&wfx_sdio_driver); + return ret; +} +module_init(wfx_core_init); + +static void __exit wfx_core_exit(void) +{ + if (IS_ENABLED(CONFIG_MMC)) + sdio_unregister_driver(&wfx_sdio_driver); + if (IS_ENABLED(CONFIG_SPI)) + spi_unregister_driver(&wfx_spi_driver); +} +module_exit(wfx_core_exit); diff --git a/drivers/staging/wfx/wfx_version.h b/drivers/staging/wfx/wfx_version.h new file mode 100644 index 000000000000..6e7f30207c73 --- /dev/null +++ b/drivers/staging/wfx/wfx_version.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! */ +#define WFX_LABEL "2.3.1" From patchwork Thu Sep 19 10:52:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152137 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 181101747 for ; Thu, 19 Sep 2019 10:52:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CCF0721924 for ; Thu, 19 Sep 2019 10:52:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="O3ybS/a8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389445AbfISKwt (ORCPT ); Thu, 19 Sep 2019 06:52:49 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387501AbfISKwp (ORCPT ); Thu, 19 Sep 2019 06:52:45 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=J34JZfVvhXzatv5bX8sspTzjrtRiBEUvcUvOjohSBVKyBdbZk5SvTbObqC96Pm78hveIwCZdjj6w0LGKJ1tUzBdYNlOszfqH2HfCFTsRYStnoiTLlGz2Kx5R/u36BOOhH9KFJ99u9nKAUki9MYxBjbeiXIXIPieW+yaFQVotU7iEXRPBKmqJk4A5Qh/cNkIIZSFPtaneCufsdX1OZF5mVlZYTLAC9WvtiNILC6dbcCwAK/ERpD0auumo+PztjYpKgwk6Y1qWzq+1u4xw8Da2vmQqiwy/Qc8+p7Tcz7aPp9I+lvR1izifA1DK/oNXnDCPQx7WVKP/A5YPF6qwv5t26Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GzkOoL63Vex8BJgy6pIDrlevRSfDGd5ehTa+0O7ZxUg=; b=FGFeQ+9CzDDVTtnT44m26LYNFhXy+a6Pmw2xS8skQWumdND6+WRO4lt1dQeYNpt1OmzApnL4Nr5ZxjUe45OFeX/nn73nP2o73Ubq5/712TnHH5i775g7B/B28+LtPrwRWW4UnzTddPuQqEUbAt60Wew8cqitXE+Mbs6vjrDx+tpbQxW7moQ33jrCn7yxgu3McIVmEfX6uR6mfIhw0Ri0AMN4E+Z3Zx1YB6v3hsGGZzhCk8jjpB+Or0KRl1avM1BpI1jILz+OMjjkBQMpTpa3wEf3lLkhfsuL7Dmlvvt8eKc3z12mwQ47wxPXqYGam4buGwI7tEyiILf9UxZkAJDk3w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GzkOoL63Vex8BJgy6pIDrlevRSfDGd5ehTa+0O7ZxUg=; b=O3ybS/a8o2yNt+e3E0MmWoJF77y7SnYbiZF5BVVRGI7eTKSdBanmx9ZJPDAF4F1hu+1shD8dHlmymPCNyi/qA0rrXIjzzxOFuAtZgOD+WFqo1pVva631bbFfEFN6UE1qs8prSbkcGZdgAutZmy3c5wOOob+4jj6Fd2M96aLJ5lE= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:35 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:35 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 02/20] staging: wfx: add support for I/O access Thread-Topic: [PATCH 02/20] staging: wfx: add support for I/O access Thread-Index: AQHVbthYAz5BV9jhoUCLDh42xNi6RQ== Date: Thu, 19 Sep 2019 10:52:35 +0000 Message-ID: <20190919105153.15285-3-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 7f17e5d0-eea6-463e-98a5-08d73cef7b1c x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:873; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(966005)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(6306002)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007)(579004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 18DDf3g1l5OJr/aBDThYY0oe7hJEwpxe4jFbQfxm7TMVjJu6i3PJRIk6jJlTKdBi93Slhklrhl6RV/PL/p3s9NBisJ7Zl1CPwk0MXyh0hdQPgPcarFxnjXefkJm8wOAoyKh4DBOxP2LUDwQ6hh9m9z3iSl+j/Wy5GcMqvu4npVmHOJt0aO9XmSGHxlnme8PZvzc4sVhj3mHGP89oPziv7QOJA4f3WwvTqbOKXI3urBlsmj3jDVAdaimitJb+0YF92/Or6ZAz41zOZ/XkzLuEtByH8/iJHGxQOC0lUw7M7Kj4tFgNP7TZcKl5yJJZm99mBOZd+qk65gXnV8WQUVZPif9ZHMlPcNYu+LZj+8hBcxFjzJZ+Ai0xvg5U7B3Ve9E7OIF4zormoaBWCuJr98CXfWVEhNUgX5GnP8bCeVYxfCg= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7f17e5d0-eea6-463e-98a5-08d73cef7b1c X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:35.0509 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: WJ+pEHfdNgh6et5H7TvuFyONq9UMpqT94JaBeDg7i1/nFnGZr3Ger7mSDhD2rXGEsWgIYKU2eDwyGotybkm3gQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Introduce bus level communication layer. At this level, 7 registers can be addressed. Notice that SPI driver is able to manage chip reset. SDIO mode relies on an external driver (`mmc-pwrseq`) to reset chip. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/bus.h | 17 +++ drivers/staging/wfx/bus_sdio.c | 189 ++++++++++++++++++++++- drivers/staging/wfx/bus_spi.c | 271 ++++++++++++++++++++++++++++++++- drivers/staging/wfx/hwio.h | 48 ++++++ drivers/staging/wfx/main.c | 53 +++++++ drivers/staging/wfx/main.h | 32 ++++ drivers/staging/wfx/wfx.h | 24 +++ 7 files changed, 632 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/hwio.h create mode 100644 drivers/staging/wfx/main.h create mode 100644 drivers/staging/wfx/wfx.h diff --git a/drivers/staging/wfx/bus.h b/drivers/staging/wfx/bus.h index 8ce871a8a9ff..eb77abc09ec2 100644 --- a/drivers/staging/wfx/bus.h +++ b/drivers/staging/wfx/bus.h @@ -11,6 +11,23 @@ #include #include +#define WFX_REG_CONFIG 0x0 +#define WFX_REG_CONTROL 0x1 +#define WFX_REG_IN_OUT_QUEUE 0x2 +#define WFX_REG_AHB_DPORT 0x3 +#define WFX_REG_BASE_ADDR 0x4 +#define WFX_REG_SRAM_DPORT 0x5 +#define WFX_REG_SET_GEN_R_W 0x6 +#define WFX_REG_FRAME_OUT 0x7 + +struct hwbus_ops { + int (*copy_from_io)(void *bus_priv, unsigned int addr, void *dst, size_t count); + int (*copy_to_io)(void *bus_priv, unsigned int addr, const void *src, size_t count); + void (*lock)(void *bus_priv); + void (*unlock)(void *bus_priv); + size_t (*align_size)(void *bus_priv, size_t size); +}; + extern struct sdio_driver wfx_sdio_driver; extern struct spi_driver wfx_spi_driver; diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index 4b26c994f43c..35bcca7ec5dc 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -8,36 +8,223 @@ #include #include #include +#include #include #include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" + +static const struct wfx_platform_data wfx_sdio_pdata = { +}; + +struct wfx_sdio_priv { + struct sdio_func *func; + struct wfx_dev *core; + u8 buf_id_tx; + u8 buf_id_rx; + int of_irq; +}; + +static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id, + void *dst, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + BUG_ON(reg_id > 7); + WARN(((uintptr_t) dst) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= (bus->buf_id_rx + 1) << 7; + ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_rx = (bus->buf_id_rx + 1) % 4; + + return ret; +} + +static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id, + const void *src, size_t count) +{ + struct wfx_sdio_priv *bus = priv; + unsigned int sdio_addr = reg_id << 2; + int ret; + + BUG_ON(reg_id > 7); + WARN(((uintptr_t) src) & 3, "unaligned buffer size"); + WARN(count & 3, "unaligned buffer address"); + + /* Use queue mode buffers */ + if (reg_id == WFX_REG_IN_OUT_QUEUE) + sdio_addr |= bus->buf_id_tx << 7; + // FIXME: discards 'const' qualifier for src + ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *) src, count); + if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE) + bus->buf_id_tx = (bus->buf_id_tx + 1) % 32; + + return ret; +} + +static void wfx_sdio_lock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_claim_host(bus->func); +} + +static void wfx_sdio_unlock(void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + sdio_release_host(bus->func); +} + +static void wfx_sdio_irq_handler(struct sdio_func *func) +{ + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + if (bus->core) + /* empty */; + else + WARN(!bus->core, "race condition in driver init/deinit"); +} + +static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv) +{ + struct wfx_sdio_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + sdio_claim_host(bus->func); + sdio_release_host(bus->func); + return IRQ_HANDLED; +} + +static int wfx_sdio_irq_subscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + ret = request_irq(bus->of_irq, wfx_sdio_irq_handler_ext, + IRQF_TRIGGER_RISING, "wfx", bus); + } else { + sdio_claim_host(bus->func); + ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler); + sdio_release_host(bus->func); + } + return ret; +} + +static int wfx_sdio_irq_unsubscribe(struct wfx_sdio_priv *bus) +{ + int ret; + + if (bus->of_irq) { + free_irq(bus->of_irq, bus); + ret = 0; + } else { + sdio_claim_host(bus->func); + ret = sdio_release_irq(bus->func); + sdio_release_host(bus->func); + } + return ret; +} + +static size_t wfx_sdio_align_size(void *priv, size_t size) +{ + struct wfx_sdio_priv *bus = priv; + + return sdio_align_size(bus->func, size); +} + +static const struct hwbus_ops wfx_sdio_hwbus_ops = { + .copy_from_io = wfx_sdio_copy_from_io, + .copy_to_io = wfx_sdio_copy_to_io, + .lock = wfx_sdio_lock, + .unlock = wfx_sdio_unlock, + .align_size = wfx_sdio_align_size, +}; static const struct of_device_id wfx_sdio_of_match[]; static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct device_node *np = func->dev.of_node; + struct wfx_sdio_priv *bus; + int ret; if (func->num != 1) { dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n", func->num); return -ENODEV; } + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + if (np) { if (!of_match_node(wfx_sdio_of_match, np)) { dev_warn(&func->dev, "no compatible device found in DT\n"); return -ENODEV; } + bus->of_irq = irq_of_parse_and_map(np, 0); } else { dev_warn(&func->dev, "device is not declared in DT, features will be limited\n"); // FIXME: ignore VID/PID and only rely on device tree // return -ENODEV; } - return -EIO; // FIXME: not yet supported + + bus->func = func; + sdio_set_drvdata(func, bus); + func->card->quirks |= MMC_QUIRK_LENIENT_FN0 | MMC_QUIRK_BLKSZ_FOR_BYTE_MODE | MMC_QUIRK_BROKEN_BYTE_MODE_512; + + sdio_claim_host(func); + ret = sdio_enable_func(func); + // Block of 64 bytes is more efficient than 512B for frame sizes < 4k + sdio_set_block_size(func, 64); + sdio_release_host(func); + if (ret) + goto err0; + + ret = wfx_sdio_irq_subscribe(bus); + if (ret) + goto err1; + + bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, + &wfx_sdio_hwbus_ops, bus); + if (!bus->core) { + ret = -EIO; + goto err2; + } + + return 0; + +err2: + wfx_sdio_irq_unsubscribe(bus); +err1: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); +err0: + return ret; } static void wfx_sdio_remove(struct sdio_func *func) { + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + wfx_free_common(bus->core); + wfx_sdio_irq_unsubscribe(bus); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); } #define SDIO_VENDOR_ID_SILABS 0x0000 diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index 574b60f513e9..b311ff72cf80 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -7,19 +7,288 @@ * Copyright (c) 2010, ST-Ericsson */ #include +#include +#include +#include +#include #include +#include #include #include "bus.h" +#include "wfx.h" +#include "hwio.h" +#include "main.h" + +#define DETECT_INVALID_CTRL_ACCESS + +static int gpio_reset = -2; +module_param(gpio_reset, int, 0644); +MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); + +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +static const struct wfx_platform_data wfx_spi_pdata = { +}; + +struct wfx_spi_priv { + struct spi_device *func; + struct wfx_dev *core; + struct gpio_desc *gpio_reset; + struct work_struct request_rx; + bool need_swab; +}; + +/* + * Read of control register need a particular attention because it should be + * done only after an IRQ raise. We can detect if this event happens by reading + * control register twice (it is safe to read twice since we can garantee that + * no data acess was done since IRQ raising). In add, this function optimize it + * by doing only one SPI request. + */ +#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE) +static int wfx_spi_read_ctrl_reg(struct wfx_spi_priv *bus, u16 *dst) +{ + int i, ret = 0; + u16 tx_buf[4] = { }; + u16 rx_buf[4] = { }; + u16 tmp[2] = { }; + struct spi_message m; + struct spi_transfer t = { + .rx_buf = rx_buf, + .tx_buf = tx_buf, + .len = sizeof(tx_buf), + }; + u16 regaddr = (WFX_REG_CONTROL << 12) | (sizeof(u16) / 2) | SET_READ; + + cpu_to_le16s(regaddr); + if (bus->need_swab) + swab16s(®addr); + + tx_buf[0] = tx_buf[2] = regaddr; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + for (i = 0, tmp[0] = tmp[1] + 1; tmp[0] != tmp[1] && i < 3; i++) { + ret = spi_sync(bus->func, &m); + // Changes of gpio-wakeup can occur during control register + // access. In this case, CTRL_WLAN_READY may differs. + tmp[0] = rx_buf[1] & cpu_to_le16(~CTRL_WLAN_READY); + tmp[1] = rx_buf[3] & cpu_to_le16(~CTRL_WLAN_READY); + } + if (tmp[0] != tmp[1]) + ret = -ETIMEDOUT; + else if (i > 1) + dev_info(bus->core->dev, "success read after %d failures\n", i - 1); + + *dst = rx_buf[1]; + return ret; +} +#endif + +/* + * WFx chip read data 16bits at time and place them directly into (little + * endian) CPU register. So, chip expect byte order like "B1 B0 B3 B2" (while + * LE is "B0 B1 B2 B3" and BE is "B3 B2 B1 B0") + * + * A little endian host with bits_per_word == 16 should do the right job + * natively. The code below to support big endian host and commonly used SPI + * 8bits. + */ +static int wfx_spi_copy_from_io(void *priv, unsigned int addr, + void *dst, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2) | SET_READ; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + u16 *dst16 = dst; +#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE) + u8 *dst8 = dst; +#endif + int ret, i; + + WARN(count % 2, "buffer size must be a multiple of 2"); + +#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE) + /* Some SPI driver (and especially Raspberry one) have race conditions + * during SPI transfers. It impact last byte of transfer. Work around + * bellow try to detect and solve them. + * See https://github.com/raspberrypi/linux/issues/2200 + */ + if (addr == WFX_REG_IN_OUT_QUEUE) + dst8[count - 1] = 0xFF; +#endif + + cpu_to_le16s(®addr); + if (bus->need_swab) + swab16s(®addr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + +#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE) + /* If last byte has not been overwritten, read ctrl_reg manually + */ + if (addr == WFX_REG_IN_OUT_QUEUE && !ret && dst8[count - 1] == 0xFF) { + dev_warn(bus->core->dev, "SPI DMA error detected (and resolved)\n"); + ret = wfx_spi_read_ctrl_reg(bus, (u16 *) (dst8 + count - 2)); + } +#endif + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&dst16[i]); + return ret; +} + +static int wfx_spi_copy_to_io(void *priv, unsigned int addr, + const void *src, size_t count) +{ + struct wfx_spi_priv *bus = priv; + u16 regaddr = (addr << 12) | (count / 2); + // FIXME: use a bounce buffer + u16 *src16 = (void *) src; + int ret, i; + struct spi_message m; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + + WARN(count % 2, "buffer size must be a multiple of 2"); + WARN(regaddr & SET_READ, "bad addr or size overflow"); + + cpu_to_le16s(®addr); + + if (bus->need_swab) + swab16s(®addr); + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(bus->func, &m); + + if (bus->need_swab && addr == WFX_REG_CONFIG) + for (i = 0; i < count / 2; i++) + swab16s(&src16[i]); + return ret; +} + +static void wfx_spi_lock(void *priv) +{ +} + +static void wfx_spi_unlock(void *priv) +{ +} + +static irqreturn_t wfx_spi_irq_handler(int irq, void *priv) +{ + struct wfx_spi_priv *bus = priv; + + if (!bus->core) { + WARN(!bus->core, "race condition in driver init/deinit"); + return IRQ_NONE; + } + queue_work(system_highpri_wq, &bus->request_rx); + return IRQ_HANDLED; +} + +static void wfx_spi_request_rx(struct work_struct *work) +{ +} + +static size_t wfx_spi_align_size(void *priv, size_t size) +{ + // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned + return ALIGN(size, 4); +} + +static const struct hwbus_ops wfx_spi_hwbus_ops = { + .copy_from_io = wfx_spi_copy_from_io, + .copy_to_io = wfx_spi_copy_to_io, + .lock = wfx_spi_lock, + .unlock = wfx_spi_unlock, + .align_size = wfx_spi_align_size, +}; static int wfx_spi_probe(struct spi_device *func) { - return -EIO; + struct wfx_spi_priv *bus; + int ret; + + if (!func->bits_per_word) + func->bits_per_word = 16; + ret = spi_setup(func); + if (ret) + return ret; + // Trace below is also displayed by spi_setup() if compiled with DEBUG + dev_dbg(&func->dev, "SPI params: CS=%d, mode=%d bits/word=%d speed=%d\n", + func->chip_select, func->mode, func->bits_per_word, func->max_speed_hz); + if (func->bits_per_word != 16 && func->bits_per_word != 8) + dev_warn(&func->dev, "unusual bits/word value: %d\n", func->bits_per_word); + if (func->max_speed_hz > 49000000) + dev_warn(&func->dev, "%dHz is a very high speed\n", func->max_speed_hz); + + bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + bus->func = func; + if (func->bits_per_word == 8 || IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + bus->need_swab = true; + spi_set_drvdata(func, bus); + + bus->gpio_reset = wfx_get_gpio(&func->dev, gpio_reset, "reset"); + if (!bus->gpio_reset) { + dev_warn(&func->dev, "try to load firmware anyway\n"); + } else { + gpiod_set_value(bus->gpio_reset, 0); + udelay(100); + gpiod_set_value(bus->gpio_reset, 1); + udelay(2000); + } + + ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, + IRQF_TRIGGER_RISING, "wfx", bus); + if (ret) + return ret; + + INIT_WORK(&bus->request_rx, wfx_spi_request_rx); + bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, + &wfx_spi_hwbus_ops, bus); + if (!bus->core) + return -EIO; + + return ret; } /* Disconnect Function to be called by SPI stack when device is disconnected */ static int wfx_spi_disconnect(struct spi_device *func) { + struct wfx_spi_priv *bus = spi_get_drvdata(func); + + wfx_free_common(bus->core); + // A few IRQ will be sent during device release. Hopefully, no IRQ + // should happen after wdev/wvif are released. + devm_free_irq(&func->dev, func->irq, bus); + flush_work(&bus->request_rx); return 0; } diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h new file mode 100644 index 000000000000..c490014c1df8 --- /dev/null +++ b/drivers/staging/wfx/hwio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Low-level API. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_HWIO_H +#define WFX_HWIO_H + +#define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI +#define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO +#define CFG_ERR_BUF_UNDERRUN 0x00000002 +#define CFG_ERR_DATA_IN_TOO_LARGE 0x00000004 +#define CFG_ERR_HOST_NO_OUT_QUEUE 0x00000008 +#define CFG_ERR_BUF_OVERRUN 0x00000010 +#define CFG_ERR_DATA_OUT_TOO_LARGE 0x00000020 +#define CFG_ERR_HOST_NO_IN_QUEUE 0x00000040 +#define CFG_ERR_HOST_CRC_MISS 0x00000080 // only with SDIO +#define CFG_SPI_IGNORE_CS 0x00000080 // only with SPI +#define CFG_WORD_MODE_MASK 0x00000300 // Bytes ordering (only writable in SPI): +#define CFG_WORD_MODE0 0x00000000 // B1,B0,B3,B2 (In SPI, register address and CONFIG data always use this mode) +#define CFG_WORD_MODE1 0x00000100 // B3,B2,B1,B0 +#define CFG_WORD_MODE2 0x00000200 // B0,B1,B2,B3 (SDIO) +#define CFG_DIRECT_ACCESS_MODE 0x00000400 // Direct or queue access mode +#define CFG_PREFETCH_AHB 0x00000800 +#define CFG_DISABLE_CPU_CLK 0x00001000 +#define CFG_PREFETCH_SRAM 0x00002000 +#define CFG_CPU_RESET 0x00004000 +#define CFG_SDIO_DISABLE_IRQ 0x00008000 // only with SDIO +#define CFG_IRQ_ENABLE_DATA 0x00010000 +#define CFG_IRQ_ENABLE_WRDY 0x00020000 +#define CFG_CLK_RISE_EDGE 0x00040000 +#define CFG_SDIO_DISABLE_CRC_CHK 0x00080000 // only with SDIO +#define CFG_RESERVED 0x00F00000 +#define CFG_DEVICE_ID_MAJOR 0x07000000 +#define CFG_DEVICE_ID_RESERVED 0x78000000 +#define CFG_DEVICE_ID_TYPE 0x80000000 + +#define CTRL_NEXT_LEN_MASK 0x00000FFF +#define CTRL_WLAN_WAKEUP 0x00001000 +#define CTRL_WLAN_READY 0x00002000 + +#define IGPR_RW 0x80000000 +#define IGPR_INDEX 0x7F000000 +#define IGPR_VALUE 0x00FFFFFF + +#endif /* WFX_HWIO_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index cd69f955f531..744445ef597c 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -11,10 +11,15 @@ * Copyright (c) 2004-2006 Jean-Baptiste Note , et al. */ #include +#include +#include +#include #include #include #include +#include "main.h" +#include "wfx.h" #include "bus.h" #include "wfx_version.h" @@ -23,6 +28,54 @@ MODULE_AUTHOR("Jérôme Pouiller "); MODULE_LICENSE("GPL"); MODULE_VERSION(WFX_LABEL); +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label) +{ + struct gpio_desc *ret; + char label_buf[256]; + + if (override >= 0) { + snprintf(label_buf, sizeof(label_buf), "wfx_%s", label); + ret = ERR_PTR(devm_gpio_request_one(dev, override, GPIOF_OUT_INIT_LOW, label_buf)); + if (!ret) + ret = gpio_to_desc(override); + } else if (override == -1) { + ret = NULL; + } else { + ret = devm_gpiod_get(dev, label, GPIOD_OUT_LOW); + } + if (IS_ERR(ret) || !ret) { + if (!ret || PTR_ERR(ret) == -ENOENT) + dev_warn(dev, "gpio %s is not defined\n", label); + else + dev_warn(dev, "error while requesting gpio %s\n", label); + ret = NULL; + } else { + dev_dbg(dev, "using gpio %d for %s\n", desc_to_gpio(ret), label); + } + return ret; +} + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv) +{ + struct wfx_dev *wdev; + + wdev = devm_kmalloc(dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return NULL; + wdev->dev = dev; + wdev->hwbus_ops = hwbus_ops; + wdev->hwbus_priv = hwbus_priv; + memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + return wdev; +} + +void wfx_free_common(struct wfx_dev *wdev) +{ +} + static int __init wfx_core_init(void) { int ret = 0; diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h new file mode 100644 index 000000000000..82222edf998b --- /dev/null +++ b/drivers/staging/wfx/main.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Device probe and register. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu + * Copyright 2004-2006 Jean-Baptiste Note , et al. + */ +#ifndef WFX_MAIN_H +#define WFX_MAIN_H + +#include +#include + +#include "bus.h" + +struct wfx_dev; + +struct wfx_platform_data { +}; + +struct wfx_dev *wfx_init_common(struct device *dev, + const struct wfx_platform_data *pdata, + const struct hwbus_ops *hwbus_ops, + void *hwbus_priv); +void wfx_free_common(struct wfx_dev *wdev); + +struct gpio_desc *wfx_get_gpio(struct device *dev, int override, + const char *label); + +#endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h new file mode 100644 index 000000000000..9716acc981df --- /dev/null +++ b/drivers/staging/wfx/wfx.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common private data for Silicon Labs WFx chips. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (c) 2006, Michael Wu + * Copyright 2004-2006 Jean-Baptiste Note , et al. + */ +#ifndef WFX_H +#define WFX_H + +#include "main.h" + +struct hwbus_ops; + +struct wfx_dev { + struct wfx_platform_data pdata; + struct device *dev; + const struct hwbus_ops *hwbus_ops; + void *hwbus_priv; +}; + +#endif /* WFX_H */ From patchwork Thu Sep 19 10:52:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152171 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A476A14DB for ; Thu, 19 Sep 2019 10:55:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6E4652196E for ; Thu, 19 Sep 2019 10:55:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="OdBdQkDo" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389423AbfISKwq (ORCPT ); Thu, 19 Sep 2019 06:52:46 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2387772AbfISKwp (ORCPT ); Thu, 19 Sep 2019 06:52:45 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ZtJSvddMNGXwTM4dfLqkZRPZllz4tg3A4RXGYyH5lVk+i+iAKZBoiTXt5N/16UQtQLUsi1Vd72AanJmXCx8JgWfcJcoHM1AkkswmaQVg3ABhnu1xeWTTczYcR7HLgVyhLzdG1723Q/3y066VYq6IIhA8Td56fMAO6R9D2/Bdu5hU5LMQOYmx6NkrzkFZrA3kRra0EhvE/uhO6CfMhTiNTtgqqfb4XCsTDscRm9/VQ0cgSzU4TD89tNDaKo2T18pVjb8upCie4kOlyX/5IImIuJs8ry06ODMVrnesDxkUeWtJc5C4/pdwPUEOZaO9bj4gdptwKDxX8HSZgc9AepnVSg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=tCXM7/LEc0PxNazY0erjutIZQv0dxV/PXgn4zyXDVis=; b=lcEdTjmX0JsThNiKSFiro5YuOcoSJgsNOkVzaT4Da1T42/hsqBsF78xLmF7VM7/OYiZkcLuDDY6cnPY0KItizx9kGjtsYlaTFdfUHr3Qky8TZWY8hAUiOZE+un18pk29juQlY5LAx2a2WWcEtSe0TMv831Wi2iWnQgBa8PMfIbfIJvnra0U53FevmH3C9jYMYXs9pbVX0sz3FkRYwYHh/eS6IFokkZ6oIHb6X1c7oTKFc/IABraCfJ62PUA9R2JvGEtqFQzZ6s2/UfE6ut63c1WUDnZxsVQR1DWs9Fk9+nX2B7T3J5cjkDTjJkj1sb0Bh04BkpYNPXPijeXLLQkDZQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=tCXM7/LEc0PxNazY0erjutIZQv0dxV/PXgn4zyXDVis=; b=OdBdQkDotNdbwyPyoOM6yRRFk8pH63yB7wxXDwR0BIT6B6702gnKSOp6qI2YQteV3BSMpBBqFAyf2TWDU3MuxjdJAyWMek/z8QvnWLcoAOE2rDItspZX6A+9T3T2fde+URhOLYYSrbRjwPpqRxxGe+JAO48ZEWwWQGvb3E0b9WE= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:36 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:36 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 03/20] staging: wfx: add I/O API Thread-Topic: [PATCH 03/20] staging: wfx: add I/O API Thread-Index: AQHVbthYJEqUUjZP90O1xgnWLa0FrA== Date: Thu, 19 Sep 2019 10:52:35 +0000 Message-ID: <20190919105153.15285-4-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: c210eabe-e4ed-4688-f270-08d73cef7b6e x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:422; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: ZAt2En/3SBeOguFvNFXt0CQw+mkjvY6Jc97Y/P85asZuQdCipVEdBVUfPiZK8UeszhGvTXE4ej6onWJqFzRD7k/d/e5mX1qL3Uk5DhnwO84sNDsruA2cjA6gneRIjzcjpbn2Jx3G4zMPLO2dsmR23ljJXgE2WlgPHXYOAAo03vu5dE2GNwDtnqjSspBzGupmq44GZY3ZGPL937pzX4xm3fIGe7D3z5lnbfy9UvD/cc38meKxgPs6KpWX1rC+uW/ynhWL2LTZdFQGR+tu6bltPhomQ/bj6q0yV+QY/braLKSW2yPrBtYhzRKGm5HFfAFvpjo1r7PtnSmSRtBV43aSM2e9BG985jIN0h/Ek08wvOHjx6SGewyESotLFdCEPYmbFY58eGD8KdVddFsxTF26s5Vg50UZFIhXn0GjleZTdVE= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: c210eabe-e4ed-4688-f270-08d73cef7b6e X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:35.5307 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 9DEoQ8lIbc9RQGQYWumkhcjELsdDJUM5gfTUUiUEC+ZEDvDUuLMwEmbnA4SfQwi0j0rJvQoPfnvStFsuBweY+A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller hwio.c provides an abstraction to access different types of register of the chip. Note that only data register (aka FRAME_OUT) and control register are used normal communication. Other registers are only used during chip start up. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/hwio.c | 327 +++++++++++++++++++++++++++++++++++ drivers/staging/wfx/hwio.h | 27 +++ 3 files changed, 355 insertions(+) create mode 100644 drivers/staging/wfx/hwio.c diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 74939a5a0a1c..e860845186cf 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 wfx-y := \ + hwio.o \ main.o wfx-$(CONFIG_SPI) += bus_spi.o wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c new file mode 100644 index 000000000000..fa626a49dd8a --- /dev/null +++ b/drivers/staging/wfx/hwio.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Low-level I/O functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include + +#include "hwio.h" +#include "wfx.h" +#include "bus.h" + +/* + * Internal helpers. + * + * About CONFIG_VMAP_STACK: + * When CONFIG_VMAP_STACK is enabled, it is not possible to run DMA on stack + * allocated data. Functions below that work with registers (aka functions + * ending with "32") automatically reallocate buffers with kmalloc. However, + * functions that work with arbitrary length buffers let's caller to handle + * memory location. In doubt, enable CONFIG_DEBUG_SG to detect badly located + * buffer. + */ + +static int read32(struct wfx_dev *wdev, int reg, u32 *val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + *val = ~0; // Never return undefined value + if (!tmp) + return -ENOMEM; + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, tmp, sizeof(u32)); + if (ret >= 0) + *val = le32_to_cpu(*tmp); + kfree(tmp); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +static int write32(struct wfx_dev *wdev, int reg, u32 val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + *tmp = cpu_to_le32(val); + ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, tmp, sizeof(u32)); + kfree(tmp); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = read32(wdev, reg, val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int write32_locked(struct wfx_dev *wdev, int reg, u32 val) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = write32(wdev, reg, val); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 val) +{ + int ret; + u32 val_r, val_w; + + WARN_ON(~mask & val); + val &= mask; + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = read32(wdev, reg, &val_r); + if (ret < 0) + goto err; + val_w = (val_r & ~mask) | val; + if (val_w != val_r) { + ret = write32(wdev, reg, val_w); + } +err: + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_read(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len) +{ + int ret; + int i; + u32 cfg; + u32 prefetch; + + WARN_ON(len >= 0x2000); + WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT); + + if (reg == WFX_REG_AHB_DPORT) + prefetch = CFG_PREFETCH_AHB; + else if (reg == WFX_REG_SRAM_DPORT) + prefetch = CFG_PREFETCH_SRAM; + else + return -ENODEV; + + ret = write32(wdev, WFX_REG_BASE_ADDR, addr); + if (ret < 0) + goto err; + + ret = read32(wdev, WFX_REG_CONFIG, &cfg); + if (ret < 0) + goto err; + + ret = write32(wdev, WFX_REG_CONFIG, cfg | prefetch); + if (ret < 0) + goto err; + + for (i = 0; i < 20; i++) { + ret = read32(wdev, WFX_REG_CONFIG, &cfg); + if (ret < 0) + goto err; + if (!(cfg & prefetch)) + break; + udelay(200); + } + if (i == 20) { + ret = -ETIMEDOUT; + goto err; + } + + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, buf, len); + +err: + if (ret < 0) + memset(buf, 0xFF, len); // Never return undefined value + return ret; +} + +static int indirect_write(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len) +{ + int ret; + + WARN_ON(len >= 0x2000); + WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT); + ret = write32(wdev, WFX_REG_BASE_ADDR, addr); + if (ret < 0) + return ret; + + return wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, buf, len); +} + +static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_read(wdev, reg, addr, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len) +{ + int ret; + + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_write(wdev, reg, addr, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + return ret; +} + +static int indirect_read32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 *val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32)); + *val = cpu_to_le32(*tmp); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + kfree(tmp); + return ret; +} + +static int indirect_write32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 val) +{ + int ret; + __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL); + + if (!tmp) + return -ENOMEM; + *tmp = cpu_to_le32(val); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32)); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + kfree(tmp); + return ret; +} + +int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len) +{ + int ret; + + WARN((long) buf & 3, "%s: unaligned buffer", __func__); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len) +{ + int ret; + + WARN((long) buf & 3, "%s: unaligned buffer", __func__); + wdev->hwbus_ops->lock(wdev->hwbus_priv); + ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + wdev->hwbus_ops->unlock(wdev->hwbus_priv); + if (ret) + dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); + return ret; +} + +int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len) +{ + return indirect_read_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len); +} + +int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len) +{ + return indirect_read_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len); +} + +int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len) +{ + return indirect_write_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len); +} + +int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len) +{ + return indirect_write_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len); +} + +int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val) +{ + return indirect_read32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val); +} + +int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val) +{ + return indirect_read32_locked(wdev, WFX_REG_AHB_DPORT, addr, val); +} + +int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val) +{ + return indirect_write32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val); +} + +int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val) +{ + return indirect_write32_locked(wdev, WFX_REG_AHB_DPORT, addr, val); +} + +int config_reg_read(struct wfx_dev *wdev, u32 *val) +{ + return read32_locked(wdev, WFX_REG_CONFIG, val); +} + +int config_reg_write(struct wfx_dev *wdev, u32 val) +{ + return write32_locked(wdev, WFX_REG_CONFIG, val); +} + +int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val) +{ + return write32_bits_locked(wdev, WFX_REG_CONFIG, mask, val); +} + +int control_reg_read(struct wfx_dev *wdev, u32 *val) +{ + return read32_locked(wdev, WFX_REG_CONTROL, val); +} + +int control_reg_write(struct wfx_dev *wdev, u32 val) +{ + return write32_locked(wdev, WFX_REG_CONTROL, val); +} + +int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val) +{ + return write32_bits_locked(wdev, WFX_REG_CONTROL, mask, val); +} + +int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val) +{ + int ret; + + *val = ~0; // Never return undefined value + ret = write32_locked(wdev, WFX_REG_SET_GEN_R_W, IGPR_RW | index << 24); + if (ret) + return ret; + ret = read32_locked(wdev, WFX_REG_SET_GEN_R_W, val); + if (ret) + return ret; + *val &= IGPR_VALUE; + return ret; +} + +int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val) +{ + return write32_locked(wdev, WFX_REG_SET_GEN_R_W, index << 24 | val); +} diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h index c490014c1df8..906524f71fd1 100644 --- a/drivers/staging/wfx/hwio.h +++ b/drivers/staging/wfx/hwio.h @@ -8,6 +8,25 @@ #ifndef WFX_HWIO_H #define WFX_HWIO_H +#include + +struct wfx_dev; + +int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t buf_len); +int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t buf_len); + +int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len); +int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len); + +int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len); +int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len); + +int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val); +int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val); + +int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val); +int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val); + #define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI #define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO #define CFG_ERR_BUF_UNDERRUN 0x00000002 @@ -36,13 +55,21 @@ #define CFG_DEVICE_ID_MAJOR 0x07000000 #define CFG_DEVICE_ID_RESERVED 0x78000000 #define CFG_DEVICE_ID_TYPE 0x80000000 +int config_reg_read(struct wfx_dev *wdev, u32 *val); +int config_reg_write(struct wfx_dev *wdev, u32 val); +int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val); #define CTRL_NEXT_LEN_MASK 0x00000FFF #define CTRL_WLAN_WAKEUP 0x00001000 #define CTRL_WLAN_READY 0x00002000 +int control_reg_read(struct wfx_dev *wdev, u32 *val); +int control_reg_write(struct wfx_dev *wdev, u32 val); +int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val); #define IGPR_RW 0x80000000 #define IGPR_INDEX 0x7F000000 #define IGPR_VALUE 0x00FFFFFF +int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val); +int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val); #endif /* WFX_HWIO_H */ From patchwork Thu Sep 19 10:52:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152167 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 571C114DB for ; Thu, 19 Sep 2019 10:55:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1797D21924 for ; Thu, 19 Sep 2019 10:55:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="iTbcdE3b" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389455AbfISKwu (ORCPT ); Thu, 19 Sep 2019 06:52:50 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389412AbfISKwq (ORCPT ); Thu, 19 Sep 2019 06:52:46 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=H1JTycXEXUsXuxhFX1CzarOrIfF7YIs52p1lFyi56D8MzNIu3XYu6WeOfazXTeOBAgDj/Y+GFnpRfP/SPo6cuk2qlIrt4ORgzfbrEXtV7oaG+/IFaMiLbd4s4vLy4tCpv8WHWztXW0R3X/H2Y6c0VtmZgTYGpkW0S5L9HLCk93Mi9GGeo6dd9RRF94xG5+/OJmnsN2mVWUEIXccaYHcIZO3JPlrTbjClKMsBx8s9DCdt87EYoWootGiQqv2cSbTonn8hsYL1zHtn99SkZav68cvCLj06Yy6c48OzBBRH+WPchD0zPNVb/FjcFidl0a3vDhRi+0zlps584UeHJla0iw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ovEpaCg56NloN+xo1i3MQxEpKDcYk9rAjhnkQJ+A410=; b=a8U3x7hZAkIUMa6+otyUQdwHhmS+dlYrg06VQ7/Hw7aDCIJtxN1B+s1OXO64/r5FrXt4g+33wKZN9VXPckI4NAkX3oMOIB4yVIoRyNshILDlyqIn115jOSZZn6T3DHK9eINbMihw60fhcftgRSjSW/66sRt/4qS/MJCzXfPpnboLs0XtxFbknXvO0iNi3hRMr2+7w2cAZoyJtlnRKIuDnGu3jUUuWmpGpTx6hbaUaZWUmEZZt26tBEHYk18oNlBXWAK+VmJngqQxiG7gsv8nUrjQDjV0trAJ6sqNKebTbWPfJgQPW6aGNzDf//P9154ePK3K4JSHKUephqjoxto5QA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ovEpaCg56NloN+xo1i3MQxEpKDcYk9rAjhnkQJ+A410=; b=iTbcdE3bVQHOY6L4iQCCO908lyKzonqbpGlDW24+qI8IbvE3PK/7tKPSAeO9R7pkynHuFSLLS+EvJMU9RMQ0/5fO1PFf3+iwhiO3DepI04SzO7yM8M/bz0dc0Lxm0RomtnRIsPwzQmuwh212Kw0khRI845ydygT8j9HxuWB4M9I= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:36 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:36 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 04/20] staging: wfx: add tracepoints for I/O access Thread-Topic: [PATCH 04/20] staging: wfx: add tracepoints for I/O access Thread-Index: AQHVbthZ49yLPBF0BECGn0rUHCl/Yg== Date: Thu, 19 Sep 2019 10:52:36 +0000 Message-ID: <20190919105153.15285-5-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 6373457f-43eb-4e8e-c9b1-08d73cef7bc6 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:1265; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007)(21314003);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: PRMbmHkTBnAATyGVR1Vv01asvYqHWuL5XmKizUSXB4NKqwaMF2XzWZhiSUpmCu4X8Zi6+8iMRornz2P5/FdQoAFer64JCqVJ8EUdDq/H1mDLNY/fHg9WczBPtftBYR2V4J0y8t7VLMiF7DDt7tHs/v7UYjg8T4UywF4As13MJxd2jAIijn2WuPch4kMxQTSYlHBZy0d17/o8IfoUnvKb95A/msZS2SlziA+uBlgIFm+XKseL98cOSCLxdX16QIsfzo95GiEKy3MGFY1QOodksvwuab7xjNAk+yUxmEdr8LJgGksYV5/wfYbJb5Tj/AoioHdm3XOgUEOZNv75oImjT76/sJ0CU+maDvqlYtRBnweknEO4tSwCPmkktSRBB/8l2sN3X88MbMUaST0i3NR6d5+EllUbuBp2ePq0KDvdVd8= Content-ID: <76BBCE3922326346905C126ECD7D4B38@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6373457f-43eb-4e8e-c9b1-08d73cef7bc6 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:36.3052 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: p7bwcT6GmNk4iArdSCTlkeC/NhTQ+kXQo+iYPElQPDnucmrbiyiH9M6UT/k2uytWOkiSczrBCOx8FCwp+tYV+Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Some tracepoints are useful for debugging. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 6 +- drivers/staging/wfx/debug.c | 10 +++ drivers/staging/wfx/hwio.c | 11 +++ drivers/staging/wfx/traces.h | 154 +++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/wfx/debug.c create mode 100644 drivers/staging/wfx/traces.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index e860845186cf..330b7288ebb5 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -1,8 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 +# Necessary for CREATE_TRACE_POINTS +CFLAGS_debug.o = -I$(src) + wfx-y := \ hwio.o \ - main.o + main.o \ + debug.o wfx-$(CONFIG_SPI) += bus_spi.o wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c new file mode 100644 index 000000000000..bf44c944640d --- /dev/null +++ b/drivers/staging/wfx/debug.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Debugfs interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ + +#define CREATE_TRACE_POINTS +#include "traces.h" diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c index fa626a49dd8a..0cf52aee10e7 100644 --- a/drivers/staging/wfx/hwio.c +++ b/drivers/staging/wfx/hwio.c @@ -12,6 +12,7 @@ #include "hwio.h" #include "wfx.h" #include "bus.h" +#include "traces.h" /* * Internal helpers. @@ -63,6 +64,7 @@ static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val) wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = read32(wdev, reg, val); + _trace_io_read32(reg, *val); wdev->hwbus_ops->unlock(wdev->hwbus_priv); return ret; } @@ -73,6 +75,7 @@ static int write32_locked(struct wfx_dev *wdev, int reg, u32 val) wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = write32(wdev, reg, val); + _trace_io_write32(reg, val); wdev->hwbus_ops->unlock(wdev->hwbus_priv); return ret; } @@ -86,11 +89,13 @@ static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 val) val &= mask; wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = read32(wdev, reg, &val_r); + _trace_io_read32(reg, val_r); if (ret < 0) goto err; val_w = (val_r & ~mask) | val; if (val_w != val_r) { ret = write32(wdev, reg, val_w); + _trace_io_write32(reg, val_w); } err: wdev->hwbus_ops->unlock(wdev->hwbus_priv); @@ -166,6 +171,7 @@ static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr, void *b wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = indirect_read(wdev, reg, addr, buf, len); + _trace_io_ind_read(reg, addr, buf, len); wdev->hwbus_ops->unlock(wdev->hwbus_priv); return ret; } @@ -176,6 +182,7 @@ static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr, const wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = indirect_write(wdev, reg, addr, buf, len); + _trace_io_ind_write(reg, addr, buf, len); wdev->hwbus_ops->unlock(wdev->hwbus_priv); return ret; } @@ -190,6 +197,7 @@ static int indirect_read32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 * wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32)); *val = cpu_to_le32(*tmp); + _trace_io_ind_read32(reg, addr, *val); wdev->hwbus_ops->unlock(wdev->hwbus_priv); kfree(tmp); return ret; @@ -205,6 +213,7 @@ static int indirect_write32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 *tmp = cpu_to_le32(val); wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32)); + _trace_io_ind_write32(reg, addr, val); wdev->hwbus_ops->unlock(wdev->hwbus_priv); kfree(tmp); return ret; @@ -217,6 +226,7 @@ int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len) WARN((long) buf & 3, "%s: unaligned buffer", __func__); wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + _trace_io_read(WFX_REG_IN_OUT_QUEUE, buf, len); wdev->hwbus_ops->unlock(wdev->hwbus_priv); if (ret) dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); @@ -230,6 +240,7 @@ int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len) WARN((long) buf & 3, "%s: unaligned buffer", __func__); wdev->hwbus_ops->lock(wdev->hwbus_priv); ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len); + _trace_io_write(WFX_REG_IN_OUT_QUEUE, buf, len); wdev->hwbus_ops->unlock(wdev->hwbus_priv); if (ret) dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret); diff --git a/drivers/staging/wfx/traces.h b/drivers/staging/wfx/traces.h new file mode 100644 index 000000000000..ba97df821f1b --- /dev/null +++ b/drivers/staging/wfx/traces.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Tracepoints definitions. + * + * Copyright (c) 2018-2019, Silicon Laboratories, Inc. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM wfx + +#if !defined(_WFX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _WFX_TRACE_H + +#include +#include + +#include "bus.h" + +#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) +#define TRACE_DEFINE_ENUM(a) +#endif + +/* The hell below need some explanations. For each symbolic number, we need to + * define it with TRACE_DEFINE_ENUM() and in a list for __print_symbolic. + * + * 1. Define a new macro that call TRACE_DEFINE_ENUM(): + * + * #define xxx_name(sym) TRACE_DEFINE_ENUM(sym); + * + * 2. Define list of all symbols: + * + * #define list_names \ + * ... \ + * xxx_name(XXX) \ + * ... + * + * 3. Instanciate that list_names: + * + * list_names + * + * 4. Redefine xxx_name() as a entry of array for __print_symbolic() + * + * #undef xxx_name + * #define xxx_name(msg) { msg, #msg }, + * + * 5. list_name can now nearlu be used with __print_symbolic() but, + * __print_symbolic() dislike last comma of list. So we define a new list + * with a dummy element: + * + * #define list_for_print_symbolic list_names { -1, NULL } + */ + +#define wfx_reg_list_enum \ + wfx_reg_name(WFX_REG_CONFIG, "CONFIG") \ + wfx_reg_name(WFX_REG_CONTROL, "CONTROL") \ + wfx_reg_name(WFX_REG_IN_OUT_QUEUE, "QUEUE") \ + wfx_reg_name(WFX_REG_AHB_DPORT, "AHB") \ + wfx_reg_name(WFX_REG_BASE_ADDR, "BASE_ADDR") \ + wfx_reg_name(WFX_REG_SRAM_DPORT, "SRAM") \ + wfx_reg_name(WFX_REG_SET_GEN_R_W, "SET_GEN_R_W") \ + wfx_reg_name(WFX_REG_FRAME_OUT, "FRAME_OUT") + +#undef wfx_reg_name +#define wfx_reg_name(sym, name) TRACE_DEFINE_ENUM(sym); +wfx_reg_list_enum +#undef wfx_reg_name +#define wfx_reg_name(sym, name) { sym, name }, +#define wfx_reg_list wfx_reg_list_enum { -1, NULL } + +DECLARE_EVENT_CLASS(io_data, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len), + TP_STRUCT__entry( + __field(int, reg) + __field(int, addr) + __field(int, msg_len) + __field(int, buf_len) + __array(u8, buf, 32) + __array(u8, addr_str, 10) + ), + TP_fast_assign( + __entry->reg = reg; + __entry->addr = addr; + __entry->msg_len = len; + __entry->buf_len = min_t(int, sizeof(__entry->buf), __entry->msg_len); + memcpy(__entry->buf, io_buf, __entry->buf_len); + if (addr >= 0) + snprintf(__entry->addr_str, 10, "/%08x", addr); + else + __entry->addr_str[0] = 0; + ), + TP_printk("%s%s: %s%s (%d bytes)", + __print_symbolic(__entry->reg, wfx_reg_list), + __entry->addr_str, + __print_hex(__entry->buf, __entry->buf_len), + __entry->msg_len > sizeof(__entry->buf) ? " ..." : "", + __entry->msg_len + ) +); +DEFINE_EVENT(io_data, io_write, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len)); +#define _trace_io_ind_write(reg, addr, io_buf, len) trace_io_write(reg, addr, io_buf, len) +#define _trace_io_write(reg, io_buf, len) trace_io_write(reg, -1, io_buf, len) +DEFINE_EVENT(io_data, io_read, + TP_PROTO(int reg, int addr, const void *io_buf, size_t len), + TP_ARGS(reg, addr, io_buf, len)); +#define _trace_io_ind_read(reg, addr, io_buf, len) trace_io_read(reg, addr, io_buf, len) +#define _trace_io_read(reg, io_buf, len) trace_io_read(reg, -1, io_buf, len) + +DECLARE_EVENT_CLASS(io_data32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val), + TP_STRUCT__entry( + __field(int, reg) + __field(int, addr) + __field(int, val) + __array(u8, addr_str, 10) + ), + TP_fast_assign( + __entry->reg = reg; + __entry->addr = addr; + __entry->val = val; + if (addr >= 0) + snprintf(__entry->addr_str, 10, "/%08x", addr); + else + __entry->addr_str[0] = 0; + ), + TP_printk("%s%s: %08x", + __print_symbolic(__entry->reg, wfx_reg_list), + __entry->addr_str, + __entry->val + ) +); +DEFINE_EVENT(io_data32, io_write32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val)); +#define _trace_io_ind_write32(reg, addr, val) trace_io_write32(reg, addr, val) +#define _trace_io_write32(reg, val) trace_io_write32(reg, -1, val) +DEFINE_EVENT(io_data32, io_read32, + TP_PROTO(int reg, int addr, u32 val), + TP_ARGS(reg, addr, val)); +#define _trace_io_ind_read32(reg, addr, val) trace_io_read32(reg, addr, val) +#define _trace_io_read32(reg, val) trace_io_read32(reg, -1, val) + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE traces + +#include From patchwork Thu Sep 19 10:52:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152139 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1ED8814DB for ; Thu, 19 Sep 2019 10:52:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D2776217D6 for ; Thu, 19 Sep 2019 10:52:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="djJDmeMH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389468AbfISKwu (ORCPT ); Thu, 19 Sep 2019 06:52:50 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389414AbfISKwr (ORCPT ); Thu, 19 Sep 2019 06:52:47 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Rzefjr7D4kkZfkA7VgdqWXW8keLKhKxfqiojcNOReeW0LsAt6VvOm3TWVMd8MGWwCAiQITk/3x0NxMF8aAlOlQCtXrxJ8CuPbrZmMC8sDZmuGDwof22NabwDAfbs7hOe1lHXvoUGa7udHx/gSM6UVs7BxvcpT9kxloWuA2vaL2aFzw/SZ8WlZn4HommzGI3vei1jyDzYNC2tFLMeCbNwNnKpzydtaBC5XxBEjOCvcfi0xEwpTkvAyRBeJhNSdoRix6fnvvRjGQTltaHssoqE4gb2iO5E/IMMzwDGjv6yCITd5OVprjWUwWgBjn5NOyZMtBkk+u9NGFBnTIhjnKobdA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9OCRFr3dZAaHAsVkOav+lzSBk2+FYFN4Pe6uShfL1sU=; b=Up6UEI06T3J2qZPixBmgXmY3zr7sXcK0gC9gKEaqUkZosWcsNC6EEzwH0YVipfb5F4H3Or9KFLheO7C76V85Pe3oRnG39d5X4BRWQN7XKEu8HWdEZkTa/qqUDQ97UbSWimqP7s6Z6/aInKHdsHuy/WsuqSaVBzagT2hKybqGc52FOjzudSQhyQ6iF8+mrw44EHGqaCO9zdXQqQTme7xV4WrMJdVcev3Oyd+CCtDoe9zFNDMzSrnieNCIsA1n1UMHVWNpgrCik5eTwAUKlI3o5LKB6dDqYNq0TZLtc5GV4BOaS1bxb8tcbZ+TUaXF/qiQJ35DU32GzrKKG37UM/C9KA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9OCRFr3dZAaHAsVkOav+lzSBk2+FYFN4Pe6uShfL1sU=; b=djJDmeMHeHD2eYkIDfPyLAZ32tie+Foqr0xepoht/MnpykEX9aLgAIR+Q93ytEall1M1Afgbe7yXxBvEFPOUHO05Lj9bfbIRebrvgqb7WcidZWwwW6Go9uGnX+VJGJK3jki1wZwum/cw+eOF+EOzmdNCGm17kgjEsWLpyJXrl4s= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:37 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:37 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 05/20] staging: wfx: load firmware Thread-Topic: [PATCH 05/20] staging: wfx: load firmware Thread-Index: AQHVbthZcgowo0eqhESpvw29QXecDg== Date: Thu, 19 Sep 2019 10:52:36 +0000 Message-ID: <20190919105153.15285-6-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 36624aae-9522-476d-a0f5-08d73cef7c1e x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:3513; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(966005)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(6306002)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 6yl77AXLzbqTweEbMgD/YJHk81SoC3U+jtU4yphxWLAsVag4biPvHduBH5WUz4xN6nM/mjYaKG5NrBAcPi3Tmgmu1hROW4l0w2pJNaB06oVJpSkJsUg6LRjcmS5gOWjRqgrCztS4DL/TjzHxrrJpDgO9FPEXSN9Ygv9Czg0/NfJVQPUzkEsP7Lr8YsXrNhRpBnIVbBbqJ6NV8jn5TkGzy0Hjg0jgbBPGnbeBo9q52G4g6mKcK+Cdjb4ztr6pfc43Tx2gqI0TFzhbwCPvzvj8aICiGRhXNQdIBLbCAmbXtdFAiz8QonYq3Tq94M31lbfYgrFNbmmYTRBjPlJ/avup7G2dVHIwff0ukvoCYBiFs58iqBC1AXjXvFdZxup9QFMVP8nEMXxnX0udw7O6HK4OMvG4KSXkR0ENZAW+LdbUF50= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 36624aae-9522-476d-a0f5-08d73cef7c1e X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:36.7940 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: XnDuiXQDbyeku+44s68RWLJU6gUwMttP0JS0OV/oXiQUqBQsJp0/hB/214WjTotLRwUf9h8P531Nb3xq45TdKw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller A firmware is necessary to run the chip. wfx_init_device() is in charge of loading firmware on chip and doing low level initialization. Firmwares for WF200 are available here: https://github.com/SiliconLabs/wfx-firmware/ Note that firmware are encrypted. Driver checks that key used to encrypt firmware match with key burned into chip. Currently, "C0" key is used for production chips. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/bus_sdio.c | 8 + drivers/staging/wfx/bus_spi.c | 7 + drivers/staging/wfx/fwio.c | 397 +++++++++++++++++++++++++++++++++ drivers/staging/wfx/fwio.h | 15 ++ drivers/staging/wfx/main.c | 20 ++ drivers/staging/wfx/main.h | 10 + drivers/staging/wfx/wfx.h | 2 + 8 files changed, 460 insertions(+) create mode 100644 drivers/staging/wfx/fwio.c create mode 100644 drivers/staging/wfx/fwio.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 330b7288ebb5..e568d7a6fb06 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -5,6 +5,7 @@ CFLAGS_debug.o = -I$(src) wfx-y := \ hwio.o \ + fwio.o \ main.o \ debug.o wfx-$(CONFIG_SPI) += bus_spi.o diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index 35bcca7ec5dc..25c587fe2141 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -17,6 +17,7 @@ #include "main.h" static const struct wfx_platform_data wfx_sdio_pdata = { + .file_fw = "wfm_wf200", }; struct wfx_sdio_priv { @@ -204,8 +205,14 @@ static int wfx_sdio_probe(struct sdio_func *func, goto err2; } + ret = wfx_probe(bus->core); + if (ret) + goto err3; + return 0; +err3: + wfx_free_common(bus->core); err2: wfx_sdio_irq_unsubscribe(bus); err1: @@ -220,6 +227,7 @@ static void wfx_sdio_remove(struct sdio_func *func) { struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + wfx_release(bus->core); wfx_free_common(bus->core); wfx_sdio_irq_unsubscribe(bus); sdio_claim_host(func); diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index b311ff72cf80..c474949a32dd 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -30,6 +30,8 @@ MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); #define SET_READ 0x8000 /* usage: or operation */ static const struct wfx_platform_data wfx_spi_pdata = { + .file_fw = "wfm_wf200", + .use_rising_clk = true, }; struct wfx_spi_priv { @@ -276,6 +278,10 @@ static int wfx_spi_probe(struct spi_device *func) if (!bus->core) return -EIO; + ret = wfx_probe(bus->core); + if (ret) + wfx_free_common(bus->core); + return ret; } @@ -284,6 +290,7 @@ static int wfx_spi_disconnect(struct spi_device *func) { struct wfx_spi_priv *bus = spi_get_drvdata(func); + wfx_release(bus->core); wfx_free_common(bus->core); // A few IRQ will be sent during device release. Hopefully, no IRQ // should happen after wdev/wvif are released. diff --git a/drivers/staging/wfx/fwio.c b/drivers/staging/wfx/fwio.c new file mode 100644 index 000000000000..8963d0351ee6 --- /dev/null +++ b/drivers/staging/wfx/fwio.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Firmware loading. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include +#include + +#include "fwio.h" +#include "wfx.h" +#include "hwio.h" + +#if (KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE) +#define FIELD_GET(_mask, _reg) (typeof(_mask))(((_reg) & (_mask)) >> (__builtin_ffsll(_mask) - 1)) +#else +#include +#endif + +// Addresses below are in SRAM area +#define WFX_DNLD_FIFO 0x09004000 +#define DNLD_BLOCK_SIZE 0x0400 +#define DNLD_FIFO_SIZE 0x8000 // (32 * DNLD_BLOCK_SIZE) +// Download Control Area (DCA) +#define WFX_DCA_IMAGE_SIZE 0x0900C000 +#define WFX_DCA_PUT 0x0900C004 +#define WFX_DCA_GET 0x0900C008 +#define WFX_DCA_HOST_STATUS 0x0900C00C +#define HOST_READY 0x87654321 +#define HOST_INFO_READ 0xA753BD99 +#define HOST_UPLOAD_PENDING 0xABCDDCBA +#define HOST_UPLOAD_COMPLETE 0xD4C64A99 +#define HOST_OK_TO_JUMP 0x174FC882 +#define WFX_DCA_NCP_STATUS 0x0900C010 +#define NCP_NOT_READY 0x12345678 +#define NCP_READY 0x87654321 +#define NCP_INFO_READY 0xBD53EF99 +#define NCP_DOWNLOAD_PENDING 0xABCDDCBA +#define NCP_DOWNLOAD_COMPLETE 0xCAFEFECA +#define NCP_AUTH_OK 0xD4C64A99 +#define NCP_AUTH_FAIL 0x174FC882 +#define NCP_PUB_KEY_RDY 0x7AB41D19 +#define WFX_DCA_FW_SIGNATURE 0x0900C014 +#define FW_SIGNATURE_SIZE 0x40 +#define WFX_DCA_FW_HASH 0x0900C054 +#define FW_HASH_SIZE 0x08 +#define WFX_DCA_FW_VERSION 0x0900C05C +#define FW_VERSION_SIZE 0x04 +#define WFX_DCA_RESERVED 0x0900C060 +#define DCA_RESERVED_SIZE 0x20 +#define WFX_STATUS_INFO 0x0900C080 +#define WFX_BOOTLOADER_LABEL 0x0900C084 +#define BOOTLOADER_LABEL_SIZE 0x3C +#define WFX_PTE_INFO 0x0900C0C0 +#define PTE_INFO_KEYSET_IDX 0x0D +#define PTE_INFO_SIZE 0x10 +#define WFX_ERR_INFO 0x0900C0D0 +#define ERR_INVALID_SEC_TYPE 0x05 +#define ERR_SIG_VERIF_FAILED 0x0F +#define ERR_AES_CTRL_KEY 0x10 +#define ERR_ECC_PUB_KEY 0x11 +#define ERR_MAC_KEY 0x18 + +#define DCA_TIMEOUT 50 // milliseconds +#define WAKEUP_TIMEOUT 200 // milliseconds + +static const char * const fwio_error_strings[] = { + [ERR_INVALID_SEC_TYPE] = "Invalid section type or wrong encryption", + [ERR_SIG_VERIF_FAILED] = "Signature verification failed", + [ERR_AES_CTRL_KEY] = "AES control key not initialized", + [ERR_ECC_PUB_KEY] = "ECC public key not initialized", + [ERR_MAC_KEY] = "MAC key not initialized", +}; + +/* + * request_firmware() allocate data using vmalloc(). It is not compatible with + * underlying hardware that use DMA. Function below detect this case and + * allocate a bounce buffer if necessary. + * + * Notice that, in doubt, you can enable CONFIG_DEBUG_SG to ask kernel to + * detect this problem at runtime (else, kernel silently fail). + * + * NOTE: it may also be possible to use 'pages' from struct firmware and avoid + * bounce buffer + */ +int sram_write_dma_safe(struct wfx_dev *wdev, u32 addr, const u8 *buf, size_t len) +{ + int ret; + const u8 *tmp; + + if (!virt_addr_valid(buf)) { + tmp = kmemdup(buf, len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + } else { + tmp = buf; + } + ret = sram_buf_write(wdev, addr, tmp, len); + if (!virt_addr_valid(buf)) + kfree(tmp); + return ret; +} + +int get_firmware(struct wfx_dev *wdev, u32 keyset_chip, + const struct firmware **fw, int *file_offset) +{ + int keyset_file; + char filename[256]; + const char *data; + int ret; + + snprintf(filename, sizeof(filename), "%s_%02X.sec", wdev->pdata.file_fw, keyset_chip); +#if (KERNEL_VERSION(4, 18, 0) > LINUX_VERSION_CODE) + ret = request_firmware(fw, filename, wdev->dev); +#else + ret = firmware_request_nowarn(fw, filename, wdev->dev); +#endif + if (ret) { + dev_info(wdev->dev, "can't load %s, falling back to %s.sec\n", filename, wdev->pdata.file_fw); + snprintf(filename, sizeof(filename), "%s.sec", wdev->pdata.file_fw); + ret = request_firmware(fw, filename, wdev->dev); + if (ret) { + dev_err(wdev->dev, "can't load %s\n", filename); + *fw = NULL; + return ret; + } + } + + data = (*fw)->data; + if (memcmp(data, "KEYSET", 6) != 0) { + // Legacy firmware format + *file_offset = 0; + keyset_file = 0x90; + } else { + *file_offset = 8; + keyset_file = (hex_to_bin(data[6]) * 16) | hex_to_bin(data[7]); + if (keyset_file < 0) { + dev_err(wdev->dev, "%s corrupted\n", filename); + release_firmware(*fw); + *fw = NULL; + return -EINVAL; + } + } + if (keyset_file != keyset_chip) { + dev_err(wdev->dev, "firmware keyset is incompatible with chip (file: 0x%02X, chip: 0x%02X)\n", + keyset_file, keyset_chip); + release_firmware(*fw); + *fw = NULL; + return -ENODEV; + } + wdev->keyset = keyset_file; + return 0; +} + +static int wait_ncp_status(struct wfx_dev *wdev, u32 status) +{ + ktime_t now, start; + u32 reg; + int ret; + + start = ktime_get(); + for (;;) { + ret = sram_reg_read(wdev, WFX_DCA_NCP_STATUS, ®); + if (ret < 0) + return -EIO; + now = ktime_get(); + if (reg == status) + break; + if (ktime_after(now, ktime_add_ms(start, DCA_TIMEOUT))) + return -ETIMEDOUT; + } + if (ktime_compare(now, start)) + dev_dbg(wdev->dev, "chip answer after %lldus\n", ktime_us_delta(now, start)); + else + dev_dbg(wdev->dev, "chip answer immediately\n"); + return 0; +} + +static int upload_firmware(struct wfx_dev *wdev, const u8 *data, size_t len) +{ + int ret; + u32 offs, bytes_done; + ktime_t now, start; + + if (len % DNLD_BLOCK_SIZE) { + dev_err(wdev->dev, "firmware size is not aligned. Buffer overrun will occur\n"); + return -EIO; + } + offs = 0; + while (offs < len) { + start = ktime_get(); + for (;;) { + ret = sram_reg_read(wdev, WFX_DCA_GET, &bytes_done); + if (ret < 0) + return ret; + now = ktime_get(); + if (offs + DNLD_BLOCK_SIZE - bytes_done < DNLD_FIFO_SIZE) + break; + if (ktime_after(now, ktime_add_ms(start, DCA_TIMEOUT))) + return -ETIMEDOUT; + } + if (ktime_compare(now, start)) + dev_dbg(wdev->dev, "answer after %lldus\n", ktime_us_delta(now, start)); + + ret = sram_write_dma_safe(wdev, WFX_DNLD_FIFO + (offs % DNLD_FIFO_SIZE), + data + offs, DNLD_BLOCK_SIZE); + if (ret < 0) + return ret; + + // WFx seems to not support writing 0 in this register during + // first loop + offs += DNLD_BLOCK_SIZE; + ret = sram_reg_write(wdev, WFX_DCA_PUT, offs); + if (ret < 0) + return ret; + } + return 0; +} + +static void print_boot_status(struct wfx_dev *wdev) +{ + u32 val32; + + sram_reg_read(wdev, WFX_STATUS_INFO, &val32); + if (val32 == 0x12345678) { + dev_info(wdev->dev, "no error reported by secure boot\n"); + } else { + sram_reg_read(wdev, WFX_ERR_INFO, &val32); + if (val32 < ARRAY_SIZE(fwio_error_strings) && fwio_error_strings[val32]) + dev_info(wdev->dev, "secure boot error: %s\n", fwio_error_strings[val32]); + else + dev_info(wdev->dev, "secure boot error: Unknown (0x%02x)\n", val32); + } +} + +int load_firmware_secure(struct wfx_dev *wdev) +{ + const struct firmware *fw = NULL; + int header_size; + int fw_offset; + ktime_t start; + u8 *buf; + int ret; + + BUILD_BUG_ON(PTE_INFO_SIZE > BOOTLOADER_LABEL_SIZE); + buf = kmalloc(BOOTLOADER_LABEL_SIZE + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_READY); + ret = wait_ncp_status(wdev, NCP_INFO_READY); + if (ret) + goto error; + + sram_buf_read(wdev, WFX_BOOTLOADER_LABEL, buf, BOOTLOADER_LABEL_SIZE); + buf[BOOTLOADER_LABEL_SIZE] = 0; + dev_dbg(wdev->dev, "bootloader: \"%s\"\n", buf); + + sram_buf_read(wdev, WFX_PTE_INFO, buf, PTE_INFO_SIZE); + ret = get_firmware(wdev, buf[PTE_INFO_KEYSET_IDX], &fw, &fw_offset); + if (ret) + goto error; + header_size = fw_offset + FW_SIGNATURE_SIZE + FW_HASH_SIZE; + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_INFO_READ); + ret = wait_ncp_status(wdev, NCP_READY); + if (ret) + goto error; + + sram_reg_write(wdev, WFX_DNLD_FIFO, 0xFFFFFFFF); // Fifo init + sram_write_dma_safe(wdev, WFX_DCA_FW_VERSION, "\x01\x00\x00\x00", FW_VERSION_SIZE); + sram_write_dma_safe(wdev, WFX_DCA_FW_SIGNATURE, fw->data + fw_offset, FW_SIGNATURE_SIZE); + sram_write_dma_safe(wdev, WFX_DCA_FW_HASH, fw->data + fw_offset + FW_SIGNATURE_SIZE, FW_HASH_SIZE); + sram_reg_write(wdev, WFX_DCA_IMAGE_SIZE, fw->size - header_size); + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_UPLOAD_PENDING); + ret = wait_ncp_status(wdev, NCP_DOWNLOAD_PENDING); + if (ret) + goto error; + + start = ktime_get(); + ret = upload_firmware(wdev, fw->data + header_size, fw->size - header_size); + if (ret) + goto error; + dev_dbg(wdev->dev, "firmware load after %lldus\n", ktime_us_delta(ktime_get(), start)); + + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_UPLOAD_COMPLETE); + ret = wait_ncp_status(wdev, NCP_AUTH_OK); + // Legacy ROM support + if (ret < 0) + ret = wait_ncp_status(wdev, NCP_PUB_KEY_RDY); + if (ret < 0) + goto error; + sram_reg_write(wdev, WFX_DCA_HOST_STATUS, HOST_OK_TO_JUMP); + +error: + kfree(buf); + if (fw) + release_firmware(fw); + if (ret) + print_boot_status(wdev); + return ret; +} + +static int init_gpr(struct wfx_dev *wdev) +{ + int ret, i; + static const struct { + int index; + u32 value; + } gpr_init[] = { + { 0x07, 0x208775 }, + { 0x08, 0x2EC020 }, + { 0x09, 0x3C3C3C }, + { 0x0B, 0x322C44 }, + { 0x0C, 0xA06497 }, + }; + + for (i = 0; i < ARRAY_SIZE(gpr_init); i++) { + ret = igpr_reg_write(wdev, gpr_init[i].index, gpr_init[i].value); + if (ret < 0) + return ret; + dev_dbg(wdev->dev, " index %02x: %08x\n", gpr_init[i].index, gpr_init[i].value); + } + return 0; +} + +int wfx_init_device(struct wfx_dev *wdev) +{ + int ret; + int hw_revision, hw_type; + int wakeup_timeout = 50; // ms + ktime_t now, start; + u32 reg; + + reg = CFG_DIRECT_ACCESS_MODE | CFG_CPU_RESET | CFG_WORD_MODE2; + if (wdev->pdata.use_rising_clk) + reg |= CFG_CLK_RISE_EDGE; + ret = config_reg_write(wdev, reg); + if (ret < 0) { + dev_err(wdev->dev, "bus returned an error during first write access. Host configuration error?\n"); + return -EIO; + } + + ret = config_reg_read(wdev, ®); + if (ret < 0) { + dev_err(wdev->dev, "bus returned an error during first read access. Bus configuration error?\n"); + return -EIO; + } + if (reg == 0 || reg == ~0) { + dev_err(wdev->dev, "chip mute. Bus configuration error or chip wasn't reset?\n"); + return -EIO; + } + dev_dbg(wdev->dev, "initial config register value: %08x\n", reg); + + hw_revision = FIELD_GET(CFG_DEVICE_ID_MAJOR, reg); + if (hw_revision == 0 || hw_revision > 2) { + dev_err(wdev->dev, "bad hardware revision number: %d\n", hw_revision); + return -ENODEV; + } + hw_type = FIELD_GET(CFG_DEVICE_ID_TYPE, reg); + if (hw_type == 1) { + dev_notice(wdev->dev, "development hardware detected\n"); + wakeup_timeout = 2000; + } + + ret = init_gpr(wdev); + if (ret < 0) + return ret; + + ret = control_reg_write(wdev, CTRL_WLAN_WAKEUP); + if (ret < 0) + return -EIO; + start = ktime_get(); + for (;;) { + ret = control_reg_read(wdev, ®); + now = ktime_get(); + if (reg & CTRL_WLAN_READY) + break; + if (ktime_after(now, ktime_add_ms(start, wakeup_timeout))) { + dev_err(wdev->dev, "chip didn't wake up. Chip wasn't reset?\n"); + return -ETIMEDOUT; + } + } + dev_dbg(wdev->dev, "chip wake up after %lldus\n", ktime_us_delta(now, start)); + + ret = config_reg_write_bits(wdev, CFG_CPU_RESET, 0); + if (ret < 0) + return ret; + ret = load_firmware_secure(wdev); + if (ret < 0) + return ret; + ret = config_reg_write_bits(wdev, CFG_DIRECT_ACCESS_MODE | CFG_IRQ_ENABLE_DATA | CFG_IRQ_ENABLE_WRDY, CFG_IRQ_ENABLE_DATA); + return ret; +} diff --git a/drivers/staging/wfx/fwio.h b/drivers/staging/wfx/fwio.h new file mode 100644 index 000000000000..6028f92503fe --- /dev/null +++ b/drivers/staging/wfx/fwio.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Firmware loading. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_FWIO_H +#define WFX_FWIO_H + +struct wfx_dev; + +int wfx_init_device(struct wfx_dev *wdev); + +#endif /* WFX_FWIO_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 744445ef597c..a8ef29174232 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -20,6 +20,8 @@ #include "main.h" #include "wfx.h" +#include "fwio.h" +#include "hwio.h" #include "bus.h" #include "wfx_version.h" @@ -76,6 +78,24 @@ void wfx_free_common(struct wfx_dev *wdev) { } +int wfx_probe(struct wfx_dev *wdev) +{ + int err; + + err = wfx_init_device(wdev); + if (err) + goto err1; + + return 0; + +err1: + return err; +} + +void wfx_release(struct wfx_dev *wdev) +{ +} + static int __init wfx_core_init(void) { int ret = 0; diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index 82222edf998b..8b2526d81984 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -18,6 +18,13 @@ struct wfx_dev; struct wfx_platform_data { + /* Keyset and ".sec" extention will appended to this string */ + const char *file_fw; + /* + * if true HIF D_out is sampled on the rising edge of the clock + * (intended to be used in 50Mhz SDIO) + */ + bool use_rising_clk; }; struct wfx_dev *wfx_init_common(struct device *dev, @@ -26,6 +33,9 @@ struct wfx_dev *wfx_init_common(struct device *dev, void *hwbus_priv); void wfx_free_common(struct wfx_dev *wdev); +int wfx_probe(struct wfx_dev *wdev); +void wfx_release(struct wfx_dev *wdev); + struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label); diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 9716acc981df..56aed33291ae 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -19,6 +19,8 @@ struct wfx_dev { struct device *dev; const struct hwbus_ops *hwbus_ops; void *hwbus_priv; + + u8 keyset; }; #endif /* WFX_H */ From patchwork Thu Sep 19 10:52:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152163 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4E49B16B1 for ; Thu, 19 Sep 2019 10:54:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E76D121924 for ; Thu, 19 Sep 2019 10:54:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="anv9S0yE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389492AbfISKww (ORCPT ); Thu, 19 Sep 2019 06:52:52 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389428AbfISKwv (ORCPT ); Thu, 19 Sep 2019 06:52:51 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AjdyilxdhrddGeddpai7Toti8z378KGUphFJRWrUdQ7rLTSiGvwCu/UIi/OoKLZhw0R5mcuQ/wtAiLc/5e+84bF6SHTZN8A7F/WfrgWrDVpnxASZa0uR6tbGepaBlcohPhbPqw9xw/W4eEMdv9o2PonnYIs5fDJISBiGJyJygX5JZhNSY1QTyQPeodmNhtsbIDLQxHaejgeTfr9bKL+KsF0Y9qEFkyWDBDKKMtdcRXRugujhRASGd1RiUPe4tLQGhE347q8+CRZZ/Defw+vL6LyzomMDMB5Vzrg7VuZyQWIRfg9qZpzxMhpcSbXWJWmijavfISOYI+pJ3zGvpBuEhA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=L3s3W4Ehil6CA5+V2G34QbWjUuVntc9wMt7XW++qyvE=; b=YvvmZfQ5A8e658cBVI6/7aqMPdUjrj5MsFRIKxWr4OUIMrXHDZuCxl2Y65T5Rm9UgMT6FyoX9EIUi+Ui7nedH2jzbHa+uiaxwY9shNh5iZko1c0sN+/Tdd8G6nU/OTBSqqILigZfheII+Guj3GfEobX0Xvh63/FkklGDy3JdegQB9NVD35UCJ1/pd9uHhd3erxC1d5eQAF1XLmUXYXwSfkfLAqMoZDVo12anhyKe3GzSEo56q4w7ZgMVDFREFUGb5VydJ9ovO1/dB0hv1F5jFW/draB5ghfevU6Q5Jbj5Pw1w5ItgI9LC8XH/cN2Tjvqp2qnSAjqkF8FNaj4yd6uRA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=L3s3W4Ehil6CA5+V2G34QbWjUuVntc9wMt7XW++qyvE=; b=anv9S0yE0uMApf45zlbLu3MVhpHJntavWLMu3hGX5mSJpdXsHfR8vnxXDYBU5HvxujPn/8L536zJWeB3Fn3nKxiCLUKM7J7yb8WdSzTCAYqUEFCiH4uuwcGOYyanTsGsW4KRQORNLA9IvhKIEWzf/s4pZIGULI6XfPgApVX7w70= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:37 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:37 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 06/20] staging: wfx: import HIF API headers Thread-Topic: [PATCH 06/20] staging: wfx: import HIF API headers Thread-Index: AQHVbthZB0W9R77jY0C1V/k2W6Py+Q== Date: Thu, 19 Sep 2019 10:52:37 +0000 Message-ID: <20190919105153.15285-7-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 42383ea1-3b67-4ad5-d313-08d73cef7c79 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:78; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(51234002)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007)(579004)(559001)(569006);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 3otWO6ctzpkXkOb47vKGPL23tpmTj6Oe10L0fuRLaemcd5xAzqCd2kDuQETnEVFBd37Waz/P5Pa/+MI/9cguQ35XwYFOH/aQL0qsHONM5DGrE7qVI9mVwwy94k7RLBOmeT25SItbwjCVOma/kLJpvj6Fjp9Gd7bhaxE+yed4uG8Ah0iTQH5cppJV7NEgm9vmRL5NHvf8YMbp0f5nRvKIt4L39w5WbyvE11JjUZZusdJgxC3AtMCvYa5aueHxcHATW5JDXKggc/UrWz6gyD+8UvKOw0MiG1+k5BHH4ZHjfp9T1R8t/JQWTfy8gJDNZdLhaKw//YJ0TokfVMTBTkcR+IDbRppt87FEJllj+YzZY5mGk6Dj5BHDXKTo3QXTOodJZ1NR1g+0qNw11YkIv4GNzLm+wt35BjM7jq8Bsri4Pu8= Content-ID: <8A9B0978A3AD7349B02C052770972817@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 42383ea1-3b67-4ad5-d313-08d73cef7c79 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:37.3087 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: qQMc8Od8VI+fX7rRsZfhVzOxUgZhBUuXJsub1D+LvOos9Ni1l/EnYNkwyiKltN1Ll9b8zvKLPScd5QDiFZAsdA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller These files are shared with firmware sources. Only a subset of these definitions are used by driver but, for now, it is easier to import all. API defines 3 kinds of messages: - Requests (req) are sent from host to chip - Confirmations (cnf) are sent by chip and are always in reply to a request - Indications (ind) are spontaneous message from chip to host One request normally generate one confirmation. There are a few exceptions to this rule: - "shutdown" request is not acknowledged - multiple tx request can be acknowledged a unique "multi-tx" confirmation In add, API defines MIB. They are sub-structures for write_mib and read_mib API. Note that all numbers in API have to be little endian when sent/received from/to chip (I didn't declared them with __le32 because driver also use them internally). Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/hif_api_cmd.h | 681 ++++++++++++++++++++++++++ drivers/staging/wfx/hif_api_general.h | 437 +++++++++++++++++ drivers/staging/wfx/hif_api_mib.h | 558 +++++++++++++++++++++ 3 files changed, 1676 insertions(+) create mode 100644 drivers/staging/wfx/hif_api_cmd.h create mode 100644 drivers/staging/wfx/hif_api_general.h create mode 100644 drivers/staging/wfx/hif_api_mib.h diff --git a/drivers/staging/wfx/hif_api_cmd.h b/drivers/staging/wfx/hif_api_cmd.h new file mode 100644 index 000000000000..7c5d1ea6098d --- /dev/null +++ b/drivers/staging/wfx/hif_api_cmd.h @@ -0,0 +1,681 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_CMD_H +#define WFX_HIF_API_CMD_H + +#include "hif_api_general.h" + +#define HIF_NUM_AC 4 + +#define HIF_API_SSID_SIZE API_SSID_SIZE + +enum hif_requests_ids { + HIF_REQ_ID_RESET = 0x0a, + HIF_REQ_ID_READ_MIB = 0x05, + HIF_REQ_ID_WRITE_MIB = 0x06, + HIF_REQ_ID_START_SCAN = 0x07, + HIF_REQ_ID_STOP_SCAN = 0x08, + HIF_REQ_ID_TX = 0x04, + HIF_REQ_ID_JOIN = 0x0b, + HIF_REQ_ID_SET_PM_MODE = 0x10, + HIF_REQ_ID_SET_BSS_PARAMS = 0x11, + HIF_REQ_ID_ADD_KEY = 0x0c, + HIF_REQ_ID_REMOVE_KEY = 0x0d, + HIF_REQ_ID_EDCA_QUEUE_PARAMS = 0x13, + HIF_REQ_ID_START = 0x17, + HIF_REQ_ID_BEACON_TRANSMIT = 0x18, + HIF_REQ_ID_UPDATE_IE = 0x1b, + HIF_REQ_ID_MAP_LINK = 0x1c, +}; + +enum hif_confirmations_ids { + HIF_CNF_ID_RESET = 0x0a, + HIF_CNF_ID_READ_MIB = 0x05, + HIF_CNF_ID_WRITE_MIB = 0x06, + HIF_CNF_ID_START_SCAN = 0x07, + HIF_CNF_ID_STOP_SCAN = 0x08, + HIF_CNF_ID_TX = 0x04, + HIF_CNF_ID_MULTI_TRANSMIT = 0x1e, + HIF_CNF_ID_JOIN = 0x0b, + HIF_CNF_ID_SET_PM_MODE = 0x10, + HIF_CNF_ID_SET_BSS_PARAMS = 0x11, + HIF_CNF_ID_ADD_KEY = 0x0c, + HIF_CNF_ID_REMOVE_KEY = 0x0d, + HIF_CNF_ID_EDCA_QUEUE_PARAMS = 0x13, + HIF_CNF_ID_START = 0x17, + HIF_CNF_ID_BEACON_TRANSMIT = 0x18, + HIF_CNF_ID_UPDATE_IE = 0x1b, + HIF_CNF_ID_MAP_LINK = 0x1c, +}; + +enum hif_indications_ids { + HIF_IND_ID_RX = 0x84, + HIF_IND_ID_SCAN_CMPL = 0x86, + HIF_IND_ID_JOIN_COMPLETE = 0x8f, + HIF_IND_ID_SET_PM_MODE_CMPL = 0x89, + HIF_IND_ID_SUSPEND_RESUME_TX = 0x8c, + HIF_IND_ID_EVENT = 0x85 +}; + +union hif_commands_ids { + enum hif_requests_ids request; + enum hif_confirmations_ids confirmation; + enum hif_indications_ids indication; +}; + +enum hif_status { + HIF_STATUS_SUCCESS = 0x0, + HIF_STATUS_FAILURE = 0x1, + HIF_INVALID_PARAMETER = 0x2, + HIF_STATUS_WARNING = 0x3, + HIF_ERROR_UNSUPPORTED_MSG_ID = 0x4, + HIF_STATUS_DECRYPTFAILURE = 0x10, + HIF_STATUS_MICFAILURE = 0x11, + HIF_STATUS_NO_KEY_FOUND = 0x12, + HIF_STATUS_RETRY_EXCEEDED = 0x13, + HIF_STATUS_TX_LIFETIME_EXCEEDED = 0x14, + HIF_REQUEUE = 0x15, + HIF_STATUS_REFUSED = 0x16, + HIF_STATUS_BUSY = 0x17 +}; + +struct hif_reset_flags { + uint8_t reset_stat:1; + uint8_t reset_all_int:1; + uint8_t reserved1:6; + uint8_t reserved2[3]; +} __packed; + +struct hif_req_reset { + struct hif_reset_flags reset_flags; +} __packed; + +struct hif_cnf_reset { + uint32_t status; +} __packed; + +struct hif_req_read_mib { + uint16_t mib_id; + uint16_t reserved; +} __packed; + +struct hif_cnf_read_mib { + uint32_t status; + uint16_t mib_id; + uint16_t length; + uint8_t mib_data[]; +} __packed; + +struct hif_req_write_mib { + uint16_t mib_id; + uint16_t length; + uint8_t mib_data[]; +} __packed; + +struct hif_cnf_write_mib { + uint32_t status; +} __packed; + +struct hif_ie_flags { + uint8_t beacon:1; + uint8_t probe_resp:1; + uint8_t probe_req:1; + uint8_t reserved1:5; + uint8_t reserved2; +} __packed; + +struct hif_ie_tlv { + uint8_t type; + uint8_t length; + uint8_t data[]; +} __packed; + +struct hif_req_update_ie { + struct hif_ie_flags ie_flags; + uint16_t num_i_es; + struct hif_ie_tlv ie[]; +} __packed; + +struct hif_cnf_update_ie { + uint32_t status; +} __packed; + +struct hif_scan_type { + uint8_t type:1; + uint8_t mode:1; + uint8_t reserved:6; +} __packed; + +struct hif_scan_flags { + uint8_t fbg:1; + uint8_t reserved1:1; + uint8_t pre:1; + uint8_t reserved2:5; +} __packed; + +struct hif_auto_scan_param { + uint16_t interval; + uint8_t reserved; + int8_t rssi_thr; +} __packed; + +struct hif_ssid_def { + uint32_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; +} __packed; + +#define HIF_API_MAX_NB_SSIDS 2 +#define HIF_API_MAX_NB_CHANNELS 14 + +struct hif_req_start_scan { + uint8_t band; + struct hif_scan_type scan_type; + struct hif_scan_flags scan_flags; + uint8_t max_transmit_rate; + struct hif_auto_scan_param auto_scan_param; + uint8_t num_of_probe_requests; + uint8_t probe_delay; + uint8_t num_of_ssi_ds; + uint8_t num_of_channels; + uint32_t min_channel_time; + uint32_t max_channel_time; + int32_t tx_power_level; + uint8_t ssid_and_channel_lists[]; +} __packed; + +struct hif_start_scan_req_cstnbssid_body { + uint8_t band; + struct hif_scan_type scan_type; + struct hif_scan_flags scan_flags; + uint8_t max_transmit_rate; + struct hif_auto_scan_param auto_scan_param; + uint8_t num_of_probe_requests; + uint8_t probe_delay; + uint8_t num_of_ssi_ds; + uint8_t num_of_channels; + uint32_t min_channel_time; + uint32_t max_channel_time; + int32_t tx_power_level; + struct hif_ssid_def ssid_def[HIF_API_MAX_NB_SSIDS]; + uint8_t channel_list[]; +} __packed; + +struct hif_cnf_start_scan { + uint32_t status; +} __packed; + +struct hif_cnf_stop_scan { + uint32_t status; +} __packed; + +enum hif_pm_mode_status { + HIF_PM_MODE_ACTIVE = 0x0, + HIF_PM_MODE_PS = 0x1, + HIF_PM_MODE_UNDETERMINED = 0x2 +}; + +struct hif_ind_scan_cmpl { + uint32_t status; + uint8_t pm_mode; + uint8_t num_channels_completed; + uint16_t reserved; +} __packed; + +enum hif_queue_id { + HIF_QUEUE_ID_BACKGROUND = 0x0, + HIF_QUEUE_ID_BESTEFFORT = 0x1, + HIF_QUEUE_ID_VIDEO = 0x2, + HIF_QUEUE_ID_VOICE = 0x3 +}; + +enum hif_frame_format { + HIF_FRAME_FORMAT_NON_HT = 0x0, + HIF_FRAME_FORMAT_MIXED_FORMAT_HT = 0x1, + HIF_FRAME_FORMAT_GF_HT_11N = 0x2 +}; + +enum hif_stbc { + HIF_STBC_NOT_ALLOWED = 0x0, + HIF_STBC_ALLOWED = 0x1 +}; + +struct hif_queue { + uint8_t queue_id:2; + uint8_t peer_sta_id:4; + uint8_t reserved:2; +} __packed; + +struct hif_data_flags { + uint8_t more:1; + uint8_t fc_offset:3; + uint8_t reserved:4; +} __packed; + +struct hif_tx_flags { + uint8_t start_exp:1; + uint8_t reserved:3; + uint8_t retry_policy_index:4; +} __packed; + +struct hif_ht_tx_parameters { + uint8_t frame_format:4; + uint8_t fec_coding:1; + uint8_t short_gi:1; + uint8_t reserved1:1; + uint8_t stbc:1; + uint8_t reserved2; + uint8_t aggregation:1; + uint8_t reserved3:7; + uint8_t reserved4; +} __packed; + +struct hif_req_tx { + uint32_t packet_id; + uint8_t max_tx_rate; + struct hif_queue queue_id; + struct hif_data_flags data_flags; + struct hif_tx_flags tx_flags; + uint32_t reserved; + uint32_t expire_time; + struct hif_ht_tx_parameters ht_tx_parameters; + uint8_t frame[]; +} __packed; + +enum hif_qos_ackplcy { + HIF_QOS_ACKPLCY_NORMAL = 0x0, + HIF_QOS_ACKPLCY_TXNOACK = 0x1, + HIF_QOS_ACKPLCY_NOEXPACK = 0x2, + HIF_QOS_ACKPLCY_BLCKACK = 0x3 +}; + +struct hif_tx_result_flags { + uint8_t aggr:1; + uint8_t requeue:1; + uint8_t ack_policy:2; + uint8_t txop_limit:1; + uint8_t reserved1:3; + uint8_t reserved2; +} __packed; + +struct hif_cnf_tx { + uint32_t status; + uint32_t packet_id; + uint8_t txed_rate; + uint8_t ack_failures; + struct hif_tx_result_flags tx_result_flags; + uint32_t media_delay; + uint32_t tx_queue_delay; +} __packed; + +struct hif_cnf_multi_transmit { + uint32_t num_tx_confs; + struct hif_cnf_tx tx_conf_payload[]; +} __packed; + +enum hif_ri_flags_encrypt { + HIF_RI_FLAGS_UNENCRYPTED = 0x0, + HIF_RI_FLAGS_WEP_ENCRYPTED = 0x1, + HIF_RI_FLAGS_TKIP_ENCRYPTED = 0x2, + HIF_RI_FLAGS_AES_ENCRYPTED = 0x3, + HIF_RI_FLAGS_WAPI_ENCRYPTED = 0x4 +}; + +struct hif_rx_flags { + uint8_t encryp:3; + uint8_t in_aggr:1; + uint8_t first_aggr:1; + uint8_t last_aggr:1; + uint8_t defrag:1; + uint8_t beacon:1; + uint8_t tim:1; + uint8_t bitmap:1; + uint8_t match_ssid:1; + uint8_t match_bssid:1; + uint8_t more:1; + uint8_t reserved1:1; + uint8_t ht:1; + uint8_t stbc:1; + uint8_t match_uc_addr:1; + uint8_t match_mc_addr:1; + uint8_t match_bc_addr:1; + uint8_t key_type:1; + uint8_t key_index:4; + uint8_t reserved2:1; + uint8_t peer_sta_id:4; + uint8_t reserved3:2; + uint8_t reserved4:1; +} __packed; + +struct hif_ind_rx { + uint32_t status; + uint16_t channel_number; + uint8_t rxed_rate; + uint8_t rcpi_rssi; + struct hif_rx_flags rx_flags; + uint8_t frame[]; +} __packed; + + +struct hif_req_edca_queue_params { + uint8_t queue_id; + uint8_t reserved1; + uint8_t aifsn; + uint8_t reserved2; + uint16_t cw_min; + uint16_t cw_max; + uint16_t tx_op_limit; + uint16_t allowed_medium_time; + uint32_t reserved3; +} __packed; + +struct hif_cnf_edca_queue_params { + uint32_t status; +} __packed; + +enum hif_ap_mode { + HIF_MODE_IBSS = 0x0, + HIF_MODE_BSS = 0x1 +}; + +enum hif_preamble { + HIF_PREAMBLE_LONG = 0x0, + HIF_PREAMBLE_SHORT = 0x1, + HIF_PREAMBLE_SHORT_LONG12 = 0x2 +}; + +struct hif_join_flags { + uint8_t reserved1:2; + uint8_t force_no_beacon:1; + uint8_t force_with_ind:1; + uint8_t reserved2:4; +} __packed; + +struct hif_req_join { + uint8_t mode; + uint8_t band; + uint16_t channel_number; + uint8_t bssid[ETH_ALEN]; + uint16_t atim_window; + uint8_t preamble_type; + uint8_t probe_for_join; + uint8_t reserved; + struct hif_join_flags join_flags; + uint32_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; + uint32_t beacon_interval; + uint32_t basic_rate_set; +} __packed; + +struct hif_cnf_join { + uint32_t status; +} __packed; + +struct hif_ind_join_complete { + uint32_t status; +} __packed; + +struct hif_bss_flags { + uint8_t lost_count_only:1; + uint8_t reserved:7; +} __packed; + +struct hif_req_set_bss_params { + struct hif_bss_flags bss_flags; + uint8_t beacon_lost_count; + uint16_t aid; + uint32_t operational_rate_set; +} __packed; + +struct hif_cnf_set_bss_params { + uint32_t status; +} __packed; + +struct hif_pm_mode { + uint8_t enter_psm:1; + uint8_t reserved:6; + uint8_t fast_psm:1; +} __packed; + +struct hif_req_set_pm_mode { + struct hif_pm_mode pm_mode; + uint8_t fast_psm_idle_period; + uint8_t ap_psm_change_period; + uint8_t min_auto_ps_poll_period; +} __packed; + +struct hif_cnf_set_pm_mode { + uint32_t status; +} __packed; + +struct hif_ind_set_pm_mode_cmpl { + uint32_t status; + uint8_t pm_mode; + uint8_t reserved[3]; +} __packed; + + +struct hif_req_start { + uint8_t mode; + uint8_t band; + uint16_t channel_number; + uint32_t reserved1; + uint32_t beacon_interval; + uint8_t dtim_period; + uint8_t preamble_type; + uint8_t reserved2; + uint8_t ssid_length; + uint8_t ssid[HIF_API_SSID_SIZE]; + uint32_t basic_rate_set; +} __packed; + +struct hif_cnf_start { + uint32_t status; +} __packed; + +enum hif_beacon { + HIF_BEACON_STOP = 0x0, + HIF_BEACON_START = 0x1 +}; + +struct hif_req_beacon_transmit { + uint8_t enable_beaconing; + uint8_t reserved[3]; +} __packed; + +struct hif_cnf_beacon_transmit { + uint32_t status; +} __packed; + +enum hif_sta_map_direction { + HIF_STA_MAP = 0x0, + HIF_STA_UNMAP = 0x1 +}; + +struct hif_map_link_flags { + uint8_t map_direction:1; + uint8_t mfpc:1; + uint8_t reserved:6; +} __packed; + +struct hif_req_map_link { + uint8_t mac_addr[ETH_ALEN]; + struct hif_map_link_flags map_link_flags; + uint8_t peer_sta_id; +} __packed; + +struct hif_cnf_map_link { + uint32_t status; +} __packed; + +struct hif_suspend_resume_flags { + uint8_t resume:1; + uint8_t reserved1:2; + uint8_t bc_mc_only:1; + uint8_t reserved2:4; + uint8_t reserved3; +} __packed; + +struct hif_ind_suspend_resume_tx { + struct hif_suspend_resume_flags suspend_resume_flags; + uint16_t peer_sta_set; +} __packed; + + +#define MAX_KEY_ENTRIES 24 +#define HIF_API_WEP_KEY_DATA_SIZE 16 +#define HIF_API_TKIP_KEY_DATA_SIZE 16 +#define HIF_API_RX_MIC_KEY_SIZE 8 +#define HIF_API_TX_MIC_KEY_SIZE 8 +#define HIF_API_AES_KEY_DATA_SIZE 16 +#define HIF_API_WAPI_KEY_DATA_SIZE 16 +#define HIF_API_MIC_KEY_DATA_SIZE 16 +#define HIF_API_IGTK_KEY_DATA_SIZE 16 +#define HIF_API_RX_SEQUENCE_COUNTER_SIZE 8 +#define HIF_API_IPN_SIZE 8 + +enum hif_key_type { + HIF_KEY_TYPE_WEP_DEFAULT = 0x0, + HIF_KEY_TYPE_WEP_PAIRWISE = 0x1, + HIF_KEY_TYPE_TKIP_GROUP = 0x2, + HIF_KEY_TYPE_TKIP_PAIRWISE = 0x3, + HIF_KEY_TYPE_AES_GROUP = 0x4, + HIF_KEY_TYPE_AES_PAIRWISE = 0x5, + HIF_KEY_TYPE_WAPI_GROUP = 0x6, + HIF_KEY_TYPE_WAPI_PAIRWISE = 0x7, + HIF_KEY_TYPE_IGTK_GROUP = 0x8, + HIF_KEY_TYPE_NONE = 0x9 +}; + +struct hif_wep_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved; + uint8_t key_length; + uint8_t key_data[HIF_API_WEP_KEY_DATA_SIZE]; +} __packed; + +struct hif_wep_group_key { + uint8_t key_id; + uint8_t key_length; + uint8_t reserved[2]; + uint8_t key_data[HIF_API_WEP_KEY_DATA_SIZE]; +} __packed; + +struct hif_tkip_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved[2]; + uint8_t tkip_key_data[HIF_API_TKIP_KEY_DATA_SIZE]; + uint8_t rx_mic_key[HIF_API_RX_MIC_KEY_SIZE]; + uint8_t tx_mic_key[HIF_API_TX_MIC_KEY_SIZE]; +} __packed; + +struct hif_tkip_group_key { + uint8_t tkip_key_data[HIF_API_TKIP_KEY_DATA_SIZE]; + uint8_t rx_mic_key[HIF_API_RX_MIC_KEY_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t rx_sequence_counter[HIF_API_RX_SEQUENCE_COUNTER_SIZE]; +} __packed; + +struct hif_aes_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t reserved[2]; + uint8_t aes_key_data[HIF_API_AES_KEY_DATA_SIZE]; +} __packed; + +struct hif_aes_group_key { + uint8_t aes_key_data[HIF_API_AES_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t rx_sequence_counter[HIF_API_RX_SEQUENCE_COUNTER_SIZE]; +} __packed; + +struct hif_wapi_pairwise_key { + uint8_t peer_address[ETH_ALEN]; + uint8_t key_id; + uint8_t reserved; + uint8_t wapi_key_data[HIF_API_WAPI_KEY_DATA_SIZE]; + uint8_t mic_key_data[HIF_API_MIC_KEY_DATA_SIZE]; +} __packed; + +struct hif_wapi_group_key { + uint8_t wapi_key_data[HIF_API_WAPI_KEY_DATA_SIZE]; + uint8_t mic_key_data[HIF_API_MIC_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; +} __packed; + +struct hif_igtk_group_key { + uint8_t igtk_key_data[HIF_API_IGTK_KEY_DATA_SIZE]; + uint8_t key_id; + uint8_t reserved[3]; + uint8_t ipn[HIF_API_IPN_SIZE]; +} __packed; + +union hif_privacy_key_data { + struct hif_wep_pairwise_key wep_pairwise_key; + struct hif_wep_group_key wep_group_key; + struct hif_tkip_pairwise_key tkip_pairwise_key; + struct hif_tkip_group_key tkip_group_key; + struct hif_aes_pairwise_key aes_pairwise_key; + struct hif_aes_group_key aes_group_key; + struct hif_wapi_pairwise_key wapi_pairwise_key; + struct hif_wapi_group_key wapi_group_key; + struct hif_igtk_group_key igtk_group_key; +}; + +struct hif_req_add_key { + uint8_t type; + uint8_t entry_index; + uint8_t int_id:2; + uint8_t reserved1:6; + uint8_t reserved2; + union hif_privacy_key_data key; +} __packed; + +struct hif_cnf_add_key { + uint32_t status; +} __packed; + +struct hif_req_remove_key { + uint8_t entry_index; + uint8_t reserved[3]; +} __packed; + +struct hif_cnf_remove_key { + uint32_t status; +} __packed; + +enum hif_event_ind { + HIF_EVENT_IND_BSSLOST = 0x1, + HIF_EVENT_IND_BSSREGAINED = 0x2, + HIF_EVENT_IND_RCPI_RSSI = 0x3, + HIF_EVENT_IND_PS_MODE_ERROR = 0x4, + HIF_EVENT_IND_INACTIVITY = 0x5 +}; + +enum hif_ps_mode_error { + HIF_PS_ERROR_NO_ERROR = 0, + HIF_PS_ERROR_AP_NOT_RESP_TO_POLL = 1, + HIF_PS_ERROR_AP_NOT_RESP_TO_UAPSD_TRIGGER = 2, + HIF_PS_ERROR_AP_SENT_UNICAST_IN_DOZE = 3, + HIF_PS_ERROR_AP_NO_DATA_AFTER_TIM = 4 +}; + +union hif_event_data { + uint8_t rcpi_rssi; + uint32_t ps_mode_error; + uint32_t peer_sta_set; +}; + +struct hif_ind_event { + uint32_t event_id; + union hif_event_data event_data; +} __packed; + + +#endif diff --git a/drivers/staging/wfx/hif_api_general.h b/drivers/staging/wfx/hif_api_general.h new file mode 100644 index 000000000000..d885b55d2882 --- /dev/null +++ b/drivers/staging/wfx/hif_api_general.h @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_GENERAL_H +#define WFX_HIF_API_GENERAL_H + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#define __packed __attribute__((__packed__)) +#endif + +#define API_SSID_SIZE 32 + +#define HIF_ID_IS_INDICATION 0x80 +#define HIF_COUNTER_MAX 7 + +struct hif_msg { + uint16_t len; + uint8_t id; + uint8_t reserved:1; + uint8_t interface:2; + uint8_t seqnum:3; + uint8_t encrypted:2; + uint8_t body[]; +} __packed; + +enum hif_general_requests_ids { + HIF_REQ_ID_CONFIGURATION = 0x09, + HIF_REQ_ID_CONTROL_GPIO = 0x26, + HIF_REQ_ID_SET_SL_MAC_KEY = 0x27, + HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS = 0x28, + HIF_REQ_ID_SL_CONFIGURE = 0x29, + HIF_REQ_ID_PREVENT_ROLLBACK = 0x2a, + HIF_REQ_ID_PTA_SETTINGS = 0x2b, + HIF_REQ_ID_PTA_PRIORITY = 0x2c, + HIF_REQ_ID_PTA_STATE = 0x2d, + HIF_REQ_ID_SHUT_DOWN = 0x32, +}; + +enum hif_general_confirmations_ids { + HIF_CNF_ID_CONFIGURATION = 0x09, + HIF_CNF_ID_CONTROL_GPIO = 0x26, + HIF_CNF_ID_SET_SL_MAC_KEY = 0x27, + HIF_CNF_ID_SL_EXCHANGE_PUB_KEYS = 0x28, + HIF_CNF_ID_SL_CONFIGURE = 0x29, + HIF_CNF_ID_PREVENT_ROLLBACK = 0x2a, + HIF_CNF_ID_PTA_SETTINGS = 0x2b, + HIF_CNF_ID_PTA_PRIORITY = 0x2c, + HIF_CNF_ID_PTA_STATE = 0x2d, + HIF_CNF_ID_SHUT_DOWN = 0x32, +}; + +enum hif_general_indications_ids { + HIF_IND_ID_EXCEPTION = 0xe0, + HIF_IND_ID_STARTUP = 0xe1, + HIF_IND_ID_WAKEUP = 0xe2, + HIF_IND_ID_GENERIC = 0xe3, + HIF_IND_ID_ERROR = 0xe4, + HIF_IND_ID_SL_EXCHANGE_PUB_KEYS = 0xe5 +}; + +enum hif_hi_status { + HI_STATUS_SUCCESS = 0x0000, + HI_STATUS_FAILURE = 0x0001, + HI_INVALID_PARAMETER = 0x0002, + HI_STATUS_GPIO_WARNING = 0x0003, + HI_ERROR_UNSUPPORTED_MSG_ID = 0x0004, + SL_MAC_KEY_STATUS_SUCCESS = 0x005A, + SL_MAC_KEY_STATUS_FAILED_KEY_ALREADY_BURNED = 0x006B, + SL_MAC_KEY_STATUS_FAILED_RAM_MODE_NOT_ALLOWED = 0x007C, + SL_MAC_KEY_STATUS_FAILED_UNKNOWN_MODE = 0x008D, + SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS = 0x009E, + SL_PUB_KEY_EXCHANGE_STATUS_FAILED = 0x00AF, + PREVENT_ROLLBACK_CNF_SUCCESS = 0x1234, + PREVENT_ROLLBACK_CNF_WRONG_MAGIC_WORD = 0x1256 +}; + +enum hif_api_rate_index { + API_RATE_INDEX_B_1MBPS = 0, + API_RATE_INDEX_B_2MBPS = 1, + API_RATE_INDEX_B_5P5MBPS = 2, + API_RATE_INDEX_B_11MBPS = 3, + API_RATE_INDEX_PBCC_22MBPS = 4, + API_RATE_INDEX_PBCC_33MBPS = 5, + API_RATE_INDEX_G_6MBPS = 6, + API_RATE_INDEX_G_9MBPS = 7, + API_RATE_INDEX_G_12MBPS = 8, + API_RATE_INDEX_G_18MBPS = 9, + API_RATE_INDEX_G_24MBPS = 10, + API_RATE_INDEX_G_36MBPS = 11, + API_RATE_INDEX_G_48MBPS = 12, + API_RATE_INDEX_G_54MBPS = 13, + API_RATE_INDEX_N_6P5MBPS = 14, + API_RATE_INDEX_N_13MBPS = 15, + API_RATE_INDEX_N_19P5MBPS = 16, + API_RATE_INDEX_N_26MBPS = 17, + API_RATE_INDEX_N_39MBPS = 18, + API_RATE_INDEX_N_52MBPS = 19, + API_RATE_INDEX_N_58P5MBPS = 20, + API_RATE_INDEX_N_65MBPS = 21, + API_RATE_NUM_ENTRIES = 22 +}; + + +enum hif_fw_type { + HIF_FW_TYPE_ETF = 0x0, + HIF_FW_TYPE_WFM = 0x1, + HIF_FW_TYPE_WSM = 0x2 +}; + +struct hif_capabilities { + uint8_t link_mode:2; + uint8_t reserved1:6; + uint8_t reserved2; + uint8_t reserved3; + uint8_t reserved4; +} __packed; + +struct hif_otp_regul_sel_mode_info { + uint8_t region_sel_mode:4; + uint8_t reserved:4; +} __packed; + +struct hif_otp_phy_info { + uint8_t phy1_region:3; + uint8_t phy0_region:3; + uint8_t otp_phy_ver:2; +} __packed; + +#define API_OPN_SIZE 14 +#define API_UID_SIZE 8 +#define API_DISABLED_CHANNEL_LIST_SIZE 2 +#define API_FIRMWARE_LABEL_SIZE 128 + +struct hif_ind_startup { + uint32_t status; + uint16_t hardware_id; + uint8_t opn[API_OPN_SIZE]; + uint8_t uid[API_UID_SIZE]; + uint16_t num_inp_ch_bufs; + uint16_t size_inp_ch_buf; + uint8_t num_links_ap; + uint8_t num_interfaces; + uint8_t mac_addr[2][ETH_ALEN]; + uint8_t api_version_minor; + uint8_t api_version_major; + struct hif_capabilities capabilities; + uint8_t firmware_build; + uint8_t firmware_minor; + uint8_t firmware_major; + uint8_t firmware_type; + uint8_t disabled_channel_list[API_DISABLED_CHANNEL_LIST_SIZE]; + struct hif_otp_regul_sel_mode_info regul_sel_mode_info; + struct hif_otp_phy_info otp_phy_info; + uint32_t supported_rate_mask; + uint8_t firmware_label[API_FIRMWARE_LABEL_SIZE]; +} __packed; + +struct hif_ind_wakeup { +} __packed; + +struct hif_req_configuration { + uint16_t length; + uint8_t pds_data[]; +} __packed; + +struct hif_cnf_configuration { + uint32_t status; +} __packed; + +enum hif_gpio_mode { + HIF_GPIO_MODE_D0 = 0x0, + HIF_GPIO_MODE_D1 = 0x1, + HIF_GPIO_MODE_OD0 = 0x2, + HIF_GPIO_MODE_OD1 = 0x3, + HIF_GPIO_MODE_TRISTATE = 0x4, + HIF_GPIO_MODE_TOGGLE = 0x5, + HIF_GPIO_MODE_READ = 0x6 +}; + +struct hif_req_control_gpio { + uint8_t gpio_label; + uint8_t gpio_mode; +} __packed; + +enum hif_gpio_error { + HIF_GPIO_ERROR_0 = 0x0, + HIF_GPIO_ERROR_1 = 0x1, + HIF_GPIO_ERROR_2 = 0x2 +}; + +struct hif_cnf_control_gpio { + uint32_t status; + uint32_t value; +} __packed; + +enum hif_generic_indication_type { + HIF_GENERIC_INDICATION_TYPE_RAW = 0x0, + HIF_GENERIC_INDICATION_TYPE_STRING = 0x1, + HIF_GENERIC_INDICATION_TYPE_RX_STATS = 0x2 +}; + +struct hif_rx_stats { + uint32_t nb_rx_frame; + uint32_t nb_crc_frame; + uint32_t per_total; + uint32_t throughput; + uint32_t nb_rx_by_rate[API_RATE_NUM_ENTRIES]; + uint16_t per[API_RATE_NUM_ENTRIES]; + int16_t snr[API_RATE_NUM_ENTRIES]; + int16_t rssi[API_RATE_NUM_ENTRIES]; + int16_t cfo[API_RATE_NUM_ENTRIES]; + uint32_t date; + uint32_t pwr_clk_freq; + uint8_t is_ext_pwr_clk; + int8_t current_temp; +} __packed; + +union hif_indication_data { + struct hif_rx_stats rx_stats; + uint8_t raw_data[1]; +}; + +struct hif_ind_generic { + uint32_t indication_type; + union hif_indication_data indication_data; +} __packed; + + +#define HIF_EXCEPTION_DATA_SIZE 124 + +struct hif_ind_exception { + uint8_t data[HIF_EXCEPTION_DATA_SIZE]; +} __packed; + + +enum hif_error { + HIF_ERROR_FIRMWARE_ROLLBACK = 0x0, + HIF_ERROR_FIRMWARE_DEBUG_ENABLED = 0x1, + HIF_ERROR_OUTDATED_SESSION_KEY = 0x2, + HIF_ERROR_INVALID_SESSION_KEY = 0x3, + HIF_ERROR_OOR_VOLTAGE = 0x4, + HIF_ERROR_PDS_VERSION = 0x5, + HIF_ERROR_OOR_TEMPERATURE = 0x6, + HIF_ERROR_REQ_DURING_KEY_EXCHANGE = 0x7, + HIF_ERROR_MULTI_TX_CNF_SECURELINK = 0x8, + HIF_ERROR_SECURELINK_OVERFLOW = 0x9, + HIF_ERROR_SECURELINK_DECRYPTION = 0xa +}; + +struct hif_ind_error { + uint32_t type; + uint8_t data[]; +} __packed; + +enum hif_secure_link_state { + SEC_LINK_UNAVAILABLE = 0x0, + SEC_LINK_RESERVED = 0x1, + SEC_LINK_EVAL = 0x2, + SEC_LINK_ENFORCED = 0x3 +}; + +enum hif_sl_encryption_type { + NO_ENCRYPTION = 0, + TX_ENCRYPTION = 1, + RX_ENCRYPTION = 2, + HP_ENCRYPTION = 3 +}; + +struct hif_sl_msg_hdr { + uint32_t seqnum:30; + uint32_t encrypted:2; +} __packed; + +struct hif_sl_msg { + struct hif_sl_msg_hdr hdr; + uint16_t len; + uint8_t payload[]; +} __packed; + +#define AES_CCM_TAG_SIZE 16 + +struct hif_sl_tag { + uint8_t tag[16]; +} __packed; + +enum hif_sl_mac_key_dest { + SL_MAC_KEY_DEST_OTP = 0x78, + SL_MAC_KEY_DEST_RAM = 0x87 +}; + +#define API_KEY_VALUE_SIZE 32 + +struct hif_req_set_sl_mac_key { + uint8_t otp_or_ram; + uint8_t key_value[API_KEY_VALUE_SIZE]; +} __packed; + +struct hif_cnf_set_sl_mac_key { + uint32_t status; +} __packed; + +#define API_HOST_PUB_KEY_SIZE 32 +#define API_HOST_PUB_KEY_MAC_SIZE 64 + +enum hif_sl_session_key_alg { + HIF_SL_CURVE25519 = 0x01, + HIF_SL_KDF = 0x02 +}; + +struct hif_req_sl_exchange_pub_keys { + uint8_t algorithm:2; + uint8_t reserved1:6; + uint8_t reserved2[3]; + uint8_t host_pub_key[API_HOST_PUB_KEY_SIZE]; + uint8_t host_pub_key_mac[API_HOST_PUB_KEY_MAC_SIZE]; +} __packed; + +struct hif_cnf_sl_exchange_pub_keys { + uint32_t status; +} __packed; + +#define API_NCP_PUB_KEY_SIZE 32 +#define API_NCP_PUB_KEY_MAC_SIZE 64 + +struct hif_ind_sl_exchange_pub_keys { + uint32_t status; + uint8_t ncp_pub_key[API_NCP_PUB_KEY_SIZE]; + uint8_t ncp_pub_key_mac[API_NCP_PUB_KEY_MAC_SIZE]; +} __packed; + +#define API_ENCR_BMP_SIZE 32 + +struct hif_req_sl_configure { + uint8_t encr_bmp[API_ENCR_BMP_SIZE]; + uint8_t disable_session_key_protection:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +struct hif_cnf_sl_configure { + uint32_t status; +} __packed; + +struct hif_req_prevent_rollback { + uint32_t magic_word; +} __packed; + +struct hif_cnf_prevent_rollback { + uint32_t status; +} __packed; + +enum hif_pta_mode { + PTA_1W_WLAN_MASTER = 0, + PTA_1W_COEX_MASTER = 1, + PTA_2W = 2, + PTA_3W = 3, + PTA_4W = 4 +}; + +enum hif_signal_level { + SIGNAL_LOW = 0, + SIGNAL_HIGH = 1 +}; + +enum hif_coex_type { + COEX_TYPE_GENERIC = 0, + COEX_TYPE_BLE = 1 +}; + +enum hif_grant_state { + NO_GRANT = 0, + GRANT = 1 +}; + +struct hif_req_pta_settings { + uint8_t pta_mode; + uint8_t request_signal_active_level; + uint8_t priority_signal_active_level; + uint8_t freq_signal_active_level; + uint8_t grant_signal_active_level; + uint8_t coex_type; + uint8_t default_grant_state; + uint8_t simultaneous_rx_accesses; + uint8_t priority_sampling_time; + uint8_t tx_rx_sampling_time; + uint8_t freq_sampling_time; + uint8_t grant_valid_time; + uint8_t fem_control_time; + uint8_t first_slot_time; + uint16_t periodic_tx_rx_sampling_time; + uint16_t coex_quota; + uint16_t wlan_quota; +} __packed; + +struct hif_cnf_pta_settings { + uint32_t status; +} __packed; + +enum hif_pta_priority { + HIF_PTA_PRIORITY_COEX_MAXIMIZED = 0x00000562, + HIF_PTA_PRIORITY_COEX_HIGH = 0x00000462, + HIF_PTA_PRIORITY_BALANCED = 0x00001461, + HIF_PTA_PRIORITY_WLAN_HIGH = 0x00001851, + HIF_PTA_PRIORITY_WLAN_MAXIMIZED = 0x00001A51 +}; + +struct hif_req_pta_priority { + uint32_t priority; +} __packed; + +struct hif_cnf_pta_priority { + uint32_t status; +} __packed; + +enum hif_pta_state { + PTA_OFF = 0, + PTA_ON = 1 +}; + +struct hif_req_pta_state { + uint32_t pta_state; +} __packed; + +struct hif_cnf_pta_state { + uint32_t status; +} __packed; + +#endif diff --git a/drivers/staging/wfx/hif_api_mib.h b/drivers/staging/wfx/hif_api_mib.h new file mode 100644 index 000000000000..3c56ef2978a2 --- /dev/null +++ b/drivers/staging/wfx/hif_api_mib.h @@ -0,0 +1,558 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* + * WFx hardware interface definitions + * + * Copyright (c) 2018-2019, Silicon Laboratories Inc. + */ + +#ifndef WFX_HIF_API_MIB_H +#define WFX_HIF_API_MIB_H + +#include "hif_api_general.h" + +#define HIF_API_IPV4_ADDRESS_SIZE 4 +#define HIF_API_IPV6_ADDRESS_SIZE 16 + +enum hif_mib_ids { + HIF_MIB_ID_GL_OPERATIONAL_POWER_MODE = 0x2000, + HIF_MIB_ID_GL_BLOCK_ACK_INFO = 0x2001, + HIF_MIB_ID_GL_SET_MULTI_MSG = 0x2002, + HIF_MIB_ID_CCA_CONFIG = 0x2003, + HIF_MIB_ID_ETHERTYPE_DATAFRAME_CONDITION = 0x2010, + HIF_MIB_ID_PORT_DATAFRAME_CONDITION = 0x2011, + HIF_MIB_ID_MAGIC_DATAFRAME_CONDITION = 0x2012, + HIF_MIB_ID_MAC_ADDR_DATAFRAME_CONDITION = 0x2013, + HIF_MIB_ID_IPV4_ADDR_DATAFRAME_CONDITION = 0x2014, + HIF_MIB_ID_IPV6_ADDR_DATAFRAME_CONDITION = 0x2015, + HIF_MIB_ID_UC_MC_BC_DATAFRAME_CONDITION = 0x2016, + HIF_MIB_ID_CONFIG_DATA_FILTER = 0x2017, + HIF_MIB_ID_SET_DATA_FILTERING = 0x2018, + HIF_MIB_ID_ARP_IP_ADDRESSES_TABLE = 0x2019, + HIF_MIB_ID_NS_IP_ADDRESSES_TABLE = 0x201A, + HIF_MIB_ID_RX_FILTER = 0x201B, + HIF_MIB_ID_BEACON_FILTER_TABLE = 0x201C, + HIF_MIB_ID_BEACON_FILTER_ENABLE = 0x201D, + HIF_MIB_ID_GRP_SEQ_COUNTER = 0x2030, + HIF_MIB_ID_TSF_COUNTER = 0x2031, + HIF_MIB_ID_STATISTICS_TABLE = 0x2032, + HIF_MIB_ID_COUNTERS_TABLE = 0x2033, + HIF_MIB_ID_MAX_TX_POWER_LEVEL = 0x2034, + HIF_MIB_ID_EXTENDED_COUNTERS_TABLE = 0x2035, + HIF_MIB_ID_DOT11_MAC_ADDRESS = 0x2040, + HIF_MIB_ID_DOT11_MAX_TRANSMIT_MSDU_LIFETIME = 0x2041, + HIF_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME = 0x2042, + HIF_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID = 0x2043, + HIF_MIB_ID_DOT11_RTS_THRESHOLD = 0x2044, + HIF_MIB_ID_SLOT_TIME = 0x2045, + HIF_MIB_ID_CURRENT_TX_POWER_LEVEL = 0x2046, + HIF_MIB_ID_NON_ERP_PROTECTION = 0x2047, + HIF_MIB_ID_TEMPLATE_FRAME = 0x2048, + HIF_MIB_ID_BEACON_WAKEUP_PERIOD = 0x2049, + HIF_MIB_ID_RCPI_RSSI_THRESHOLD = 0x204A, + HIF_MIB_ID_BLOCK_ACK_POLICY = 0x204B, + HIF_MIB_ID_OVERRIDE_INTERNAL_TX_RATE = 0x204C, + HIF_MIB_ID_SET_ASSOCIATION_MODE = 0x204D, + HIF_MIB_ID_SET_UAPSD_INFORMATION = 0x204E, + HIF_MIB_ID_SET_TX_RATE_RETRY_POLICY = 0x204F, + HIF_MIB_ID_PROTECTED_MGMT_POLICY = 0x2050, + HIF_MIB_ID_SET_HT_PROTECTION = 0x2051, + HIF_MIB_ID_KEEP_ALIVE_PERIOD = 0x2052, + HIF_MIB_ID_ARP_KEEP_ALIVE_PERIOD = 0x2053, + HIF_MIB_ID_INACTIVITY_TIMER = 0x2054, + HIF_MIB_ID_INTERFACE_PROTECTION = 0x2055, + HIF_MIB_ID_BEACON_STATS = 0x2056, +}; + +#define HIF_OP_POWER_MODE_MASK 0xf + +enum hif_op_power_mode { + HIF_OP_POWER_MODE_ACTIVE = 0x0, + HIF_OP_POWER_MODE_DOZE = 0x1, + HIF_OP_POWER_MODE_QUIESCENT = 0x2 +}; + +struct hif_mib_gl_operational_power_mode { + uint8_t power_mode:4; + uint8_t reserved1:3; + uint8_t wup_ind_activation:1; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_gl_block_ack_info { + uint8_t rx_buffer_size; + uint8_t rx_max_num_agreements; + uint8_t tx_buffer_size; + uint8_t tx_max_num_agreements; +} __packed; + +struct hif_mib_gl_set_multi_msg { + uint8_t enable_multi_tx_conf:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +enum hif_cca_thr_mode { + HIF_CCA_THR_MODE_RELATIVE = 0x0, + HIF_CCA_THR_MODE_ABSOLUTE = 0x1 +}; + +struct hif_mib_gl_cca_config { + uint8_t cca_thr_mode; + uint8_t reserved[3]; +} __packed; + +#define MAX_NUMBER_DATA_FILTERS 0xA + +#define MAX_NUMBER_IPV4_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_IPV6_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_MAC_ADDR_CONDITIONS 0x4 +#define MAX_NUMBER_UC_MC_BC_CONDITIONS 0x4 +#define MAX_NUMBER_ETHER_TYPE_CONDITIONS 0x4 +#define MAX_NUMBER_PORT_CONDITIONS 0x4 +#define MAX_NUMBER_MAGIC_CONDITIONS 0x4 +#define MAX_NUMBER_ARP_CONDITIONS 0x2 +#define MAX_NUMBER_NS_CONDITIONS 0x2 + +struct hif_mib_ethertype_data_frame_condition { + uint8_t condition_idx; + uint8_t reserved; + uint16_t ether_type; +} __packed; + +enum hif_udp_tcp_protocol { + HIF_PROTOCOL_UDP = 0x0, + HIF_PROTOCOL_TCP = 0x1, + HIF_PROTOCOL_BOTH_UDP_TCP = 0x2 +}; + +enum hif_which_port { + HIF_PORT_DST = 0x0, + HIF_PORT_SRC = 0x1, + HIF_PORT_SRC_OR_DST = 0x2 +}; + +struct hif_mib_ports_data_frame_condition { + uint8_t condition_idx; + uint8_t protocol; + uint8_t which_port; + uint8_t reserved1; + uint16_t port_number; + uint8_t reserved2[2]; +} __packed; + +#define HIF_API_MAGIC_PATTERN_SIZE 32 + +struct hif_mib_magic_data_frame_condition { + uint8_t condition_idx; + uint8_t offset; + uint8_t magic_pattern_length; + uint8_t reserved; + uint8_t magic_pattern[HIF_API_MAGIC_PATTERN_SIZE]; +} __packed; + +enum hif_mac_addr_type { + HIF_MAC_ADDR_A1 = 0x0, + HIF_MAC_ADDR_A2 = 0x1, + HIF_MAC_ADDR_A3 = 0x2 +}; + +struct hif_mib_mac_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_type; + uint8_t mac_address[ETH_ALEN]; +} __packed; + +enum hif_ip_addr_mode { + HIF_IP_ADDR_SRC = 0x0, + HIF_IP_ADDR_DST = 0x1 +}; + +struct hif_mib_ipv4_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_mode; + uint8_t reserved[2]; + uint8_t i_pv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_ipv6_addr_data_frame_condition { + uint8_t condition_idx; + uint8_t address_mode; + uint8_t reserved[2]; + uint8_t i_pv6_address[HIF_API_IPV6_ADDRESS_SIZE]; +} __packed; + +union hif_addr_type { + uint8_t value; + struct { + uint8_t type_unicast:1; + uint8_t type_multicast:1; + uint8_t type_broadcast:1; + uint8_t reserved:5; + } bits; +}; + +struct hif_mib_uc_mc_bc_data_frame_condition { + uint8_t condition_idx; + union hif_addr_type param; + uint8_t reserved[2]; +} __packed; + +struct hif_mib_config_data_filter { + uint8_t filter_idx; + uint8_t enable; + uint8_t reserved1[2]; + uint8_t eth_type_cond; + uint8_t port_cond; + uint8_t magic_cond; + uint8_t mac_cond; + uint8_t ipv4_cond; + uint8_t ipv6_cond; + uint8_t uc_mc_bc_cond; + uint8_t reserved2; +} __packed; + +struct hif_mib_set_data_filtering { + uint8_t default_filter; + uint8_t enable; + uint8_t reserved[2]; +} __packed; + +enum hif_arp_ns_frame_treatment { + HIF_ARP_NS_FILTERING_DISABLE = 0x0, + HIF_ARP_NS_FILTERING_ENABLE = 0x1, + HIF_ARP_NS_REPLY_ENABLE = 0x2 +}; + +struct hif_mib_arp_ip_addr_table { + uint8_t condition_idx; + uint8_t arp_enable; + uint8_t reserved[2]; + uint8_t ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_ns_ip_addr_table { + uint8_t condition_idx; + uint8_t ns_enable; + uint8_t reserved[2]; + uint8_t ipv6_address[HIF_API_IPV6_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_rx_filter { + uint8_t reserved1:1; + uint8_t bssid_filter:1; + uint8_t reserved2:1; + uint8_t fwd_probe_req:1; + uint8_t keep_alive_filter:1; + uint8_t reserved3:3; + uint8_t reserved4[3]; +} __packed; + +#define HIF_API_OUI_SIZE 3 +#define HIF_API_MATCH_DATA_SIZE 3 + +struct hif_ie_table_entry { + uint8_t ie_id; + uint8_t has_changed:1; + uint8_t no_longer:1; + uint8_t has_appeared:1; + uint8_t reserved:1; + uint8_t num_match_data:4; + uint8_t oui[HIF_API_OUI_SIZE]; + uint8_t match_data[HIF_API_MATCH_DATA_SIZE]; +} __packed; + +struct hif_mib_bcn_filter_table { + uint32_t num_of_info_elmts; + struct hif_ie_table_entry ie_table[]; +} __packed; + +enum hif_beacon_filter { + HIF_BEACON_FILTER_DISABLE = 0x0, + HIF_BEACON_FILTER_ENABLE = 0x1, + HIF_BEACON_FILTER_AUTO_ERP = 0x2 +}; + +struct hif_mib_bcn_filter_enable { + uint32_t enable; + uint32_t bcn_count; +} __packed; + +struct hif_mib_group_seq_counter { + uint32_t bits4716; + uint16_t bits1500; + uint16_t reserved; +} __packed; + +struct hif_mib_tsf_counter { + uint32_t tsf_counterlo; + uint32_t tsf_counterhi; +} __packed; + +struct hif_mib_stats_table { + int16_t latest_snr; + uint8_t latest_rcpi; + int8_t latest_rssi; +} __packed; + +struct hif_mib_extended_count_table { + uint32_t count_plcp_errors; + uint32_t count_fcs_errors; + uint32_t count_tx_packets; + uint32_t count_rx_packets; + uint32_t count_rx_packet_errors; + uint32_t count_rx_decryption_failures; + uint32_t count_rx_mic_failures; + uint32_t count_rx_no_key_failures; + uint32_t count_tx_multicast_frames; + uint32_t count_tx_frames_success; + uint32_t count_tx_frame_failures; + uint32_t count_tx_frames_retried; + uint32_t count_tx_frames_multi_retried; + uint32_t count_rx_frame_duplicates; + uint32_t count_rts_success; + uint32_t count_rts_failures; + uint32_t count_ack_failures; + uint32_t count_rx_multicast_frames; + uint32_t count_rx_frames_success; + uint32_t count_rx_cmacicv_errors; + uint32_t count_rx_cmac_replays; + uint32_t count_rx_mgmt_ccmp_replays; + uint32_t count_rx_bipmic_errors; + uint32_t count_rx_beacon; + uint32_t count_miss_beacon; + uint32_t reserved[15]; +} __packed; + +struct hif_mib_count_table { + uint32_t count_plcp_errors; + uint32_t count_fcs_errors; + uint32_t count_tx_packets; + uint32_t count_rx_packets; + uint32_t count_rx_packet_errors; + uint32_t count_rx_decryption_failures; + uint32_t count_rx_mic_failures; + uint32_t count_rx_no_key_failures; + uint32_t count_tx_multicast_frames; + uint32_t count_tx_frames_success; + uint32_t count_tx_frame_failures; + uint32_t count_tx_frames_retried; + uint32_t count_tx_frames_multi_retried; + uint32_t count_rx_frame_duplicates; + uint32_t count_rts_success; + uint32_t count_rts_failures; + uint32_t count_ack_failures; + uint32_t count_rx_multicast_frames; + uint32_t count_rx_frames_success; + uint32_t count_rx_cmacicv_errors; + uint32_t count_rx_cmac_replays; + uint32_t count_rx_mgmt_ccmp_replays; + uint32_t count_rx_bipmic_errors; +} __packed; + +struct hif_mib_max_tx_power_level { + int32_t max_tx_power_level_rf_port1; + int32_t max_tx_power_level_rf_port2; +} __packed; + +struct hif_mib_beacon_stats { + int32_t latest_tbtt_diff; + uint32_t reserved[4]; +} __packed; + +struct hif_mib_mac_address { + uint8_t mac_addr[ETH_ALEN]; + uint16_t reserved; +} __packed; + +struct hif_mib_dot11_max_transmit_msdu_lifetime { + uint32_t max_life_time; +} __packed; + +struct hif_mib_dot11_max_receive_lifetime { + uint32_t max_life_time; +} __packed; + +struct hif_mib_wep_default_key_id { + uint8_t wep_default_key_id; + uint8_t reserved[3]; +} __packed; + +struct hif_mib_dot11_rts_threshold { + uint32_t threshold; +} __packed; + +struct hif_mib_slot_time { + uint32_t slot_time; +} __packed; + +struct hif_mib_current_tx_power_level { + int32_t power_level; +} __packed; + +struct hif_mib_non_erp_protection { + uint8_t use_cts_to_self:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +enum hif_tx_mode { + HIF_TX_MODE_MIXED = 0x0, + HIF_TX_MODE_GREENFIELD = 0x1 +}; + +enum hif_tmplt { + HIF_TMPLT_PRBREQ = 0x0, + HIF_TMPLT_BCN = 0x1, + HIF_TMPLT_NULL = 0x2, + HIF_TMPLT_QOSNUL = 0x3, + HIF_TMPLT_PSPOLL = 0x4, + HIF_TMPLT_PRBRES = 0x5, + HIF_TMPLT_ARP = 0x6, + HIF_TMPLT_NA = 0x7 +}; + +#define HIF_API_MAX_TEMPLATE_FRAME_SIZE 700 + +struct hif_mib_template_frame { + uint8_t frame_type; + uint8_t init_rate:7; + uint8_t mode:1; + uint16_t frame_length; + uint8_t frame[HIF_API_MAX_TEMPLATE_FRAME_SIZE]; +} __packed; + +struct hif_mib_beacon_wake_up_period { + uint8_t wakeup_period_min; + uint8_t receive_dtim:1; + uint8_t reserved1:7; + uint8_t wakeup_period_max; + uint8_t reserved2; +} __packed; + +struct hif_mib_rcpi_rssi_threshold { + uint8_t detection:1; + uint8_t rcpi_rssi:1; + uint8_t upperthresh:1; + uint8_t lowerthresh:1; + uint8_t reserved:4; + uint8_t lower_threshold; + uint8_t upper_threshold; + uint8_t rolling_average_count; +} __packed; + +#define DEFAULT_BA_MAX_RX_BUFFER_SIZE 16 + +struct hif_mib_block_ack_policy { + uint8_t block_ack_tx_tid_policy; + uint8_t reserved1; + uint8_t block_ack_rx_tid_policy; + uint8_t block_ack_rx_max_buffer_size; +} __packed; + +struct hif_mib_override_int_rate { + uint8_t internal_tx_rate; + uint8_t non_erp_internal_tx_rate; + uint8_t reserved[2]; +} __packed; + +enum hif_mpdu_start_spacing { + HIF_MPDU_START_SPACING_NO_RESTRIC = 0x0, + HIF_MPDU_START_SPACING_QUARTER = 0x1, + HIF_MPDU_START_SPACING_HALF = 0x2, + HIF_MPDU_START_SPACING_ONE = 0x3, + HIF_MPDU_START_SPACING_TWO = 0x4, + HIF_MPDU_START_SPACING_FOUR = 0x5, + HIF_MPDU_START_SPACING_EIGHT = 0x6, + HIF_MPDU_START_SPACING_SIXTEEN = 0x7 +}; + +struct hif_mib_set_association_mode { + uint8_t preambtype_use:1; + uint8_t mode:1; + uint8_t rateset:1; + uint8_t spacing:1; + uint8_t reserved:4; + uint8_t preamble_type; + uint8_t mixed_or_greenfield_type; + uint8_t mpdu_start_spacing; + uint32_t basic_rate_set; +} __packed; + +struct hif_mib_set_uapsd_information { + uint8_t trig_bckgrnd:1; + uint8_t trig_be:1; + uint8_t trig_video:1; + uint8_t trig_voice:1; + uint8_t reserved1:4; + uint8_t deliv_bckgrnd:1; + uint8_t deliv_be:1; + uint8_t deliv_video:1; + uint8_t deliv_voice:1; + uint8_t reserved2:4; + uint16_t min_auto_trigger_interval; + uint16_t max_auto_trigger_interval; + uint16_t auto_trigger_step; +} __packed; + +struct hif_mib_tx_rate_retry_policy { + uint8_t policy_index; + uint8_t short_retry_count; + uint8_t long_retry_count; + uint8_t first_rate_sel:2; + uint8_t terminate:1; + uint8_t count_init:1; + uint8_t reserved1:4; + uint8_t rate_recovery_count; + uint8_t reserved2[3]; + uint8_t rates[12]; +} __packed; + +#define HIF_MIB_NUM_TX_RATE_RETRY_POLICIES 16 + +struct hif_mib_set_tx_rate_retry_policy { + uint8_t num_tx_rate_policies; + uint8_t reserved[3]; + struct hif_mib_tx_rate_retry_policy tx_rate_retry_policy[]; +} __packed; + +struct hif_mib_protected_mgmt_policy { + uint8_t pmf_enable:1; + uint8_t unpmf_allowed:1; + uint8_t host_enc_auth_frames:1; + uint8_t reserved1:5; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_set_ht_protection { + uint8_t dual_cts_prot:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + +struct hif_mib_keep_alive_period { + uint16_t keep_alive_period; + uint8_t reserved[2]; +} __packed; + +struct hif_mib_arp_keep_alive_period { + uint16_t arp_keep_alive_period; + uint8_t encr_type; + uint8_t reserved; + uint8_t sender_ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; + uint8_t target_ipv4_address[HIF_API_IPV4_ADDRESS_SIZE]; +} __packed; + +struct hif_mib_inactivity_timer { + uint8_t min_active_time; + uint8_t max_active_time; + uint16_t reserved; +} __packed; + +struct hif_mib_interface_protection { + uint8_t use_cts_prot:1; + uint8_t reserved1:7; + uint8_t reserved2[3]; +} __packed; + + +#endif From patchwork Thu Sep 19 10:52:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152165 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5BB7516B1 for ; Thu, 19 Sep 2019 10:54:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1DAB321924 for ; Thu, 19 Sep 2019 10:54:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="azlZK24m" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389755AbfISKyu (ORCPT ); Thu, 19 Sep 2019 06:54:50 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389438AbfISKwu (ORCPT ); Thu, 19 Sep 2019 06:52:50 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=MZPXAMGU3WrOcePGtY4dvZdhnS3eU1SjIKuDXjPd6OBpITTTWa6qGtY/vGzlSEWUNDrbrzlyerO+o4Ix+672csg2FhZz78rwDKq/e5DBwhpWmVQIl8JlOcrZxktlZ9SrhhVS5H2LAdQU4VKkxLlW5EJEkcozgKkRgx8S5biv7+5yvcXv8n+Xeu3LaZSzdJM8QxzyDnR8lCNrCsrsbWbJxGV2H/LVLN12rR6v+3Da6CyYsSJafR5wK8WYcswRiMoXLa/7zVm7xCQYHjN3gU7PZoQMG9tCwOWmNwFi5yHaGQ6G/+mh+GmPdRJFMZV3J28zz5F0Vp6bYGCAxfT8y5ELTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=FNetZ1+im1b4m13t59Ec9MWVOz7kGu89CmnRsIgi600=; b=ZjcUbLGNSf27bIURJ4lmjhdC1oHRPucRH5b0+EErozBfIzbCIWGgaz4NBjRveBk+kx6MAFTu7bk58+Cw3TfuLha9M9x70pp5EgG5jhHAd+ZFiBMyqhJOFyY3Cf37zQgO8Fv4bUa0ZBQ3gYVgxLUbsAAdDMk1Yi539OToEVcuHLM+duPD6ox4LXteA0NK4w9JekHx+xRp6qT33WHOAylvZ5Q0wpxL2eexaKvLNZ5kFd+fvQsBGaLZX2WKlCrRqMd5jMb9K7Qv2N5TyMjRup5OSQZ9M5vtimQhthKh2g717cVHmF1an9HCKdzP89YUHpBHbSmezLF74//wmY4tKRK5eg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=FNetZ1+im1b4m13t59Ec9MWVOz7kGu89CmnRsIgi600=; b=azlZK24med+NenlspmnLIhnbJBxas/gsEJwQmCfE8mHtUud7gxDtQIkFVkxZHH0Ds+h7N7veyRfAvLCy5BpVzhK+sRekUi2cfFKC2sfOCnHJGFQEDyXT318u6weMbDhJiSxEdszDBuNOTcR9fix+sjxZ+sbsjzueqkFZ2uv9u9s= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:38 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:38 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 07/20] staging: wfx: add IRQ handling Thread-Topic: [PATCH 07/20] staging: wfx: add IRQ handling Thread-Index: AQHVbthZSqAgZ+UjRkKZkCdHQwf+3Q== Date: Thu, 19 Sep 2019 10:52:37 +0000 Message-ID: <20190919105153.15285-8-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 39e1c97d-04f4-4b51-eb9c-08d73cef7cce x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:8273; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: uxnj9qqAs3HQ0PyQn4sY+UfY/G71eQngeVL3OA+JcVMYeyH/4zS58GE5GKJGSWpRcuFtrgDjVALsNfaNZN9c+4MQdFIx0yrQiVc0HgWtapnGDE1K5wmUmAz2cPX+ioh2My83poMtXKbLt/YMnvd2/keBBG0L0Op9QJv11YeYsvX2QFp/BiPorqqxA8UEO1Pjro82IyKlstKJKj6faTY2c/okUcxgJo6WbkJfwtlXTJ4Lt1t5moa9o5cLcZ0r7JGU0DorkoT1yM/1ftaJ4J0tiYeAC/2s73+4QwntMaED/GqxzM1v5v7lgw2YcQx6SITQz+f11b6dBGWC85FhETKO/+kZ271IAXeJMHTNg4k7sLR5pVMimSccEkM9Bi+o14nf11gLOzrzzgklBG7iVhc8hzZY/KuUc+C60a7/f1zDP7Q= Content-ID: <036F09717650BC439F89A1F23AB1BF82@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 39e1c97d-04f4-4b51-eb9c-08d73cef7cce X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:37.7664 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: iqQBIPDrflc29tKbq8GUdIKowrgQDG6VSz9PLBVanM2+E/K028jrVNm/lPy3WD81W+cly8FD1BxZ/5OxZtNM4w== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller bh_work() is in charge to schedule all HIF message from/to chip. On normal operation, when an IRQ is received, driver can get size of next message in control register. In order to save control register access, when chip send a message, it also appends a copy of control register after the message (this register is not accounted in message length declared in message header, but must accounted in bus request). This copy of control register is called "piggyback". It also handles a power saving mechanism specific to WFxxx series. This mechanism is based on a GPIO called "wakeup" GPIO. Obviously, this gpio is not part of SPI/SDIO standard buses and must be declared independently (this is the main reason for why SDIO mode try to get parameters from DT). When wakeup is enabled, host can communicate with chip only if it is awake. To wake up chip, there are two cases: - host receive an IRQ from chip (chip initiate communication): host just have to set wakeup GPIO before reading data - host want to send data to chip: host set wakeup GPIO, then wait for an IRQ (in fact, wait for an empty message) and finally send data bh_work() is also in charge to track usage of chip buffers. Normally each request expect a confirmation. However, you can notice that special "multi tx" confirmation can acknowledge multiple requests at time. Finally, note that wfx_bh_request_rx() is not atomic (because of control_reg_read()). So, in SPI mode, hard-irq handler only postpone all processing to wfx_spi_request_rx(). Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/bh.c | 277 +++++++++++++++++++++++++++++++++ drivers/staging/wfx/bh.h | 32 ++++ drivers/staging/wfx/bus_sdio.c | 4 +- drivers/staging/wfx/bus_spi.c | 5 + drivers/staging/wfx/main.c | 21 +++ drivers/staging/wfx/main.h | 2 + drivers/staging/wfx/wfx.h | 4 + 8 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/wfx/bh.c create mode 100644 drivers/staging/wfx/bh.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index e568d7a6fb06..1abd3115f11d 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -4,6 +4,7 @@ CFLAGS_debug.o = -I$(src) wfx-y := \ + bh.o \ hwio.o \ fwio.o \ main.o \ diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c new file mode 100644 index 000000000000..02a42e5c1e10 --- /dev/null +++ b/drivers/staging/wfx/bh.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Interrupt bottom half (BH). + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "bh.h" +#include "wfx.h" +#include "hwio.h" +#include "hif_api_cmd.h" + +static void device_wakeup(struct wfx_dev *wdev) +{ + if (!wdev->pdata.gpio_wakeup) + return; + if (gpiod_get_value(wdev->pdata.gpio_wakeup)) + return; + + gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + if (wfx_api_older_than(wdev, 1, 4)) { + if (!completion_done(&wdev->hif.ctrl_ready)) + udelay(2000); + } else { + // completion.h does not provide any function to wait + // completion without consume it (a kind of + // wait_for_completion_done_timeout()). So we have to emulate + // it. + if (wait_for_completion_timeout(&wdev->hif.ctrl_ready, msecs_to_jiffies(2) + 1)) + complete(&wdev->hif.ctrl_ready); + else + dev_err(wdev->dev, "timeout while wake up chip\n"); + } +} + +static void device_release(struct wfx_dev *wdev) +{ + if (!wdev->pdata.gpio_wakeup) + return; + + gpiod_set_value(wdev->pdata.gpio_wakeup, 0); +} + +static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) +{ + struct sk_buff *skb; + struct hif_msg *hif; + size_t alloc_len; + size_t computed_len; + int release_count; + int piggyback = 0; + + WARN_ON(read_len < 4); + WARN(read_len > round_down(0xFFF, 2) * sizeof(u16), + "%s: request exceed WFx capability", __func__); + + // Add 2 to take into account piggyback size + alloc_len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, read_len + 2); + skb = dev_alloc_skb(alloc_len); + if (!skb) + return -ENOMEM; + + if (wfx_data_read(wdev, skb->data, alloc_len)) + goto err; + + piggyback = le16_to_cpup((u16 *) (skb->data + alloc_len - 2)); + + hif = (struct hif_msg *) skb->data; + WARN(hif->encrypted & 0x1, "unsupported encryption type"); + if (hif->encrypted == 0x2) { + BUG(); // Not yet implemented + } else { + le16_to_cpus(hif->len); + computed_len = round_up(hif->len, 2); + } + if (computed_len != read_len) { + dev_err(wdev->dev, "inconsistent message length: %zu != %zu\n", + computed_len, read_len); + print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET, 16, 1, + hif, read_len, true); + goto err; + } + + if (!(hif->id & HIF_ID_IS_INDICATION)) { + (*is_cnf)++; + if (hif->id == HIF_CNF_ID_MULTI_TRANSMIT) + release_count = le32_to_cpu(((struct hif_cnf_multi_transmit *) hif->body)->num_tx_confs); + else + release_count = 1; + WARN(wdev->hif.tx_buffers_used < release_count, "corrupted buffer counter"); + wdev->hif.tx_buffers_used -= release_count; + if (!wdev->hif.tx_buffers_used) + wake_up(&wdev->hif.tx_buffers_empty); + } + + if (hif->id != HIF_IND_ID_EXCEPTION && hif->id != HIF_IND_ID_ERROR) { + if (hif->seqnum != wdev->hif.rx_seqnum) + dev_warn(wdev->dev, "wrong message sequence: %d != %d\n", + hif->seqnum, wdev->hif.rx_seqnum); + wdev->hif.rx_seqnum = (hif->seqnum + 1) % (HIF_COUNTER_MAX + 1); + } + + skb_put(skb, hif->len); + dev_kfree_skb(skb); /* FIXME: handle received data */ + + return piggyback; + +err: + if (skb) + dev_kfree_skb(skb); + return -EIO; +} + +static int bh_work_rx(struct wfx_dev *wdev, int max_msg, int *num_cnf) +{ + size_t len; + int i; + int ctrl_reg, piggyback; + + piggyback = 0; + for (i = 0; i < max_msg; i++) { + if (piggyback & CTRL_NEXT_LEN_MASK) + ctrl_reg = piggyback; + else if (try_wait_for_completion(&wdev->hif.ctrl_ready)) + ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, 0); + else + ctrl_reg = 0; + if (!(ctrl_reg & CTRL_NEXT_LEN_MASK)) + return i; + // ctrl_reg units are 16bits words + len = (ctrl_reg & CTRL_NEXT_LEN_MASK) * 2; + piggyback = rx_helper(wdev, len, num_cnf); + if (piggyback < 0) + return i; + if (!(piggyback & CTRL_WLAN_READY)) + dev_err(wdev->dev, "unexpected piggyback value: ready bit not set: %04x\n", + piggyback); + } + if (piggyback & CTRL_NEXT_LEN_MASK) { + ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, piggyback); + complete(&wdev->hif.ctrl_ready); + if (ctrl_reg) + dev_err(wdev->dev, "unexpected IRQ happened: %04x/%04x\n", + ctrl_reg, piggyback); + } + return i; +} + +static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif) +{ + int ret; + void *data; + bool is_encrypted = false; + size_t len = le16_to_cpu(hif->len); + + BUG_ON(len < sizeof(*hif)); + + hif->seqnum = wdev->hif.tx_seqnum; + wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1); + + data = hif; + WARN(len > wdev->hw_caps.size_inp_ch_buf, + "%s: request exceed WFx capability: %zu > %d\n", __func__, + len, wdev->hw_caps.size_inp_ch_buf); + len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, len); + ret = wfx_data_write(wdev, data, len); + if (ret) + goto end; + + wdev->hif.tx_buffers_used++; +end: + if (is_encrypted) + kfree(data); +} + +static int bh_work_tx(struct wfx_dev *wdev, int max_msg) +{ + struct hif_msg *hif; + int i; + + for (i = 0; i < max_msg; i++) { + hif = NULL; + if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) { + /* FIXME: get queued data */ + } + if (!hif) + return i; + tx_helper(wdev, hif); + } + return i; +} + +/* In SDIO mode, it is necessary to make an access to a register to acknowledge + * last received message. It could be possible to restrict this acknowledge to + * SDIO mode and only if last operation was rx. + */ +static void ack_sdio_data(struct wfx_dev *wdev) +{ + uint32_t cfg_reg; + + config_reg_read(wdev, &cfg_reg); + if (cfg_reg & 0xFF) { + dev_warn(wdev->dev, "chip reports errors: %02x\n", cfg_reg & 0xFF); + config_reg_write_bits(wdev, 0xFF, 0x00); + } +} + +static void bh_work(struct work_struct *work) +{ + struct wfx_dev *wdev = container_of(work, struct wfx_dev, hif.bh); + int stats_req = 0, stats_cnf = 0, stats_ind = 0; + bool release_chip = false, last_op_is_rx = false; + int num_tx, num_rx; + + device_wakeup(wdev); + do { + num_tx = bh_work_tx(wdev, 32); + stats_req += num_tx; + if (num_tx) + last_op_is_rx = false; + num_rx = bh_work_rx(wdev, 32, &stats_cnf); + stats_ind += num_rx; + if (num_rx) + last_op_is_rx = true; + } while (num_rx || num_tx); + stats_ind -= stats_cnf; + + if (last_op_is_rx) + ack_sdio_data(wdev); + if (!wdev->hif.tx_buffers_used && !work_pending(work)) { + device_release(wdev); + release_chip = true; + } +} + +/* + * An IRQ from chip did occur + */ +void wfx_bh_request_rx(struct wfx_dev *wdev) +{ + u32 cur, prev; + + control_reg_read(wdev, &cur); + prev = atomic_xchg(&wdev->hif.ctrl_reg, cur); + complete(&wdev->hif.ctrl_ready); + queue_work(system_highpri_wq, &wdev->hif.bh); + + if (!(cur & CTRL_NEXT_LEN_MASK)) + dev_err(wdev->dev, "unexpected control register value: length field is 0: %04x\n", + cur); + if (prev != 0) + dev_err(wdev->dev, "received IRQ but previous data was not (yet) read: %04x/%04x\n", + prev, cur); +} + +/* + * Driver want to send data + */ +void wfx_bh_request_tx(struct wfx_dev *wdev) +{ + queue_work(system_highpri_wq, &wdev->hif.bh); +} + +void wfx_bh_register(struct wfx_dev *wdev) +{ + INIT_WORK(&wdev->hif.bh, bh_work); + init_completion(&wdev->hif.ctrl_ready); + init_waitqueue_head(&wdev->hif.tx_buffers_empty); +} + +void wfx_bh_unregister(struct wfx_dev *wdev) +{ + flush_work(&wdev->hif.bh); +} diff --git a/drivers/staging/wfx/bh.h b/drivers/staging/wfx/bh.h new file mode 100644 index 000000000000..93ca98424e0b --- /dev/null +++ b/drivers/staging/wfx/bh.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interrupt bottom half. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_BH_H +#define WFX_BH_H + +#include +#include +#include + +struct wfx_dev; + +struct wfx_hif { + struct work_struct bh; + struct completion ctrl_ready; + wait_queue_head_t tx_buffers_empty; + atomic_t ctrl_reg; + int rx_seqnum; + int tx_seqnum; + int tx_buffers_used; +}; + +void wfx_bh_register(struct wfx_dev *wdev); +void wfx_bh_unregister(struct wfx_dev *wdev); +void wfx_bh_request_rx(struct wfx_dev *wdev); +void wfx_bh_request_tx(struct wfx_dev *wdev); + +#endif /* WFX_BH_H */ diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index 25c587fe2141..c0c063c3cfc9 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -15,6 +15,7 @@ #include "wfx.h" #include "hwio.h" #include "main.h" +#include "bh.h" static const struct wfx_platform_data wfx_sdio_pdata = { .file_fw = "wfm_wf200", @@ -90,7 +91,7 @@ static void wfx_sdio_irq_handler(struct sdio_func *func) struct wfx_sdio_priv *bus = sdio_get_drvdata(func); if (bus->core) - /* empty */; + wfx_bh_request_rx(bus->core); else WARN(!bus->core, "race condition in driver init/deinit"); } @@ -104,6 +105,7 @@ static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv) return IRQ_NONE; } sdio_claim_host(bus->func); + wfx_bh_request_rx(bus->core); sdio_release_host(bus->func); return IRQ_HANDLED; } diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index c474949a32dd..8a9aab3e7384 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -19,6 +19,7 @@ #include "wfx.h" #include "hwio.h" #include "main.h" +#include "bh.h" #define DETECT_INVALID_CTRL_ACCESS @@ -215,6 +216,10 @@ static irqreturn_t wfx_spi_irq_handler(int irq, void *priv) static void wfx_spi_request_rx(struct work_struct *work) { + struct wfx_spi_priv *bus = + container_of(work, struct wfx_spi_priv, request_rx); + + wfx_bh_request_rx(bus->core); } static size_t wfx_spi_align_size(void *priv, size_t size) diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index a8ef29174232..f0bea053a0d9 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -23,6 +23,7 @@ #include "fwio.h" #include "hwio.h" #include "bus.h" +#include "bh.h" #include "wfx_version.h" MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); @@ -30,6 +31,21 @@ MODULE_AUTHOR("Jérôme Pouiller "); MODULE_LICENSE("GPL"); MODULE_VERSION(WFX_LABEL); +static int gpio_wakeup = -2; +module_param(gpio_wakeup, int, 0644); +MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none."); + +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) +{ + if (wdev->hw_caps.api_version_major < major) + return true; + if (wdev->hw_caps.api_version_major > major) + return false; + if (wdev->hw_caps.api_version_minor < minor) + return true; + return false; +} + struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label) { struct gpio_desc *ret; @@ -82,18 +98,23 @@ int wfx_probe(struct wfx_dev *wdev) { int err; + wfx_bh_register(wdev); + err = wfx_init_device(wdev); if (err) goto err1; + return 0; err1: + wfx_bh_unregister(wdev); return err; } void wfx_release(struct wfx_dev *wdev) { + wfx_bh_unregister(wdev); } static int __init wfx_core_init(void) diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index 8b2526d81984..f7c65999a493 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -20,6 +20,7 @@ struct wfx_dev; struct wfx_platform_data { /* Keyset and ".sec" extention will appended to this string */ const char *file_fw; + struct gpio_desc *gpio_wakeup; /* * if true HIF D_out is sampled on the rising edge of the clock * (intended to be used in 50Mhz SDIO) @@ -38,5 +39,6 @@ void wfx_release(struct wfx_dev *wdev); struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label); +bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor); #endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 56aed33291ae..4f28938fa3a6 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -10,7 +10,9 @@ #ifndef WFX_H #define WFX_H +#include "bh.h" #include "main.h" +#include "hif_api_general.h" struct hwbus_ops; @@ -21,6 +23,8 @@ struct wfx_dev { void *hwbus_priv; u8 keyset; + struct hif_ind_startup hw_caps; + struct wfx_hif hif; }; #endif /* WFX_H */ From patchwork Thu Sep 19 10:52:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152157 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D6D6C16B1 for ; Thu, 19 Sep 2019 10:54:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A0C1C217D6 for ; Thu, 19 Sep 2019 10:54:32 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="dEVBsl4U" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389526AbfISKwz (ORCPT ); Thu, 19 Sep 2019 06:52:55 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389472AbfISKwy (ORCPT ); Thu, 19 Sep 2019 06:52:54 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=WIDSGBGMRukhk57nRlL+G+Abnf2v5SGWfBK0kjSLIEx7SG42JPF59MJjppVbkZp3yQiCzlHlFAC2pKwg3z32DYvAQTdgvzv2lgkZEqvRuRZ+yRyjtH88gxmG/ioWTLtFOdBFZdlJDWziDwMpnzGUgBb524N+Lzt2yk/g3MWx8qz7kx6I9YnPFZjWr9sQ9h57ZqGj/EKa3toMQDPUs51Lnhlw7CGpuzajCK+rFvQyiQ4V0Lk+UapEjmo2LzsGr4sNf5oTZuzeRKJAc0J3cxp0FE87put68AU6v7nISaBueGG0IRzbAH/zRP4X10hO1UGIQH0Rbs98+gBSXlLN1lzT3A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5Bm1KHIPwDvk1kpyacIlCp6b2DmOV1eFPZMvv3Tcnb0=; b=CkeUwayihPYeVUaWhfsMyYymeEZKaCr4tFS7AEYpk2kYHNHT/9qQjR+ORU77Mp0XK/oWBpHfh/9z6AuZvnAPVWccTSZs3yeh8D0I0vhaNiBQLlEh383Qm71N5OfU9FDa731MPp42cx/+wkR7ytczhhf6gzgf/hNmAdaw5kSVAyp6dK96/PJxUlOTMxPg68Rqe89eZZdCZgdr7FILbgd6OZtTvtVZFR7NFDL+G6fVoJzHmZKMv8vwUgrseR7I1kEGbircgn4c1/TOHARC1m7I64w29B+ZQuHEtFZc32CaUGWyHnRW+QqWjrEZXDHbJSgfEGTws2Qel4YU/pLwnwAnAQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5Bm1KHIPwDvk1kpyacIlCp6b2DmOV1eFPZMvv3Tcnb0=; b=dEVBsl4UpgZRK910Tj0tigvx3ByiP90rcTYvO0iD7S9iZU3knW8GS6rA+QgO6VfhyFb8yl7KWccT5/wWYhe1enq7ZfVvxSTXe18YVZrX/wIqqZy/pc4Ik9u2PkHxtv+fdsxadnrEG6hd8uP74JuMVn+MLThI0baEULAD7WCTnwk= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:39 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:39 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 08/20] staging: wfx: add tracepoints for HIF Thread-Topic: [PATCH 08/20] staging: wfx: add tracepoints for HIF Thread-Index: AQHVbthaINJ+7f1/okOvkfYfWNwjcg== Date: Thu, 19 Sep 2019 10:52:38 +0000 Message-ID: <20190919105153.15285-9-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: b6494417-d63f-4c3b-294e-08d73cef7d3f x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:13; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: BJNd2Siof3WRKmlTF0jvaOOXOzS7uRJIsvBCcxQLOZZdTOU4VXnQWi4AZbN6jbgKV6j/UsbNlbq+Ylx9QuhUbMKm/pQB2l+E6sjKjYO85jwRph9QA0sA/bowzhdPoBmMbtKac3AY7HVPDKJt2X55Tog9u5X3x+OX79g1B/6ya9JLtZxcb0LA+I4X9B09Ov6z6cd+U1oKzEkcJF/4N+8fwrDh1SXm6Rk2HmQYUIY9uFDcq6mkQI4oF/sZi0YwN1m8oIElPffFi/+9BGoKAlzo6Lob2SpGBySshfSMiMnaBr72AihNIAyLOPgRAk9YbXhup3MIPHuhm4FNeOVCe6MN3DV6v59kRJChwviHUadUXKwdk7eqmzxPW23DAIuqnudxwPvCjL1U5wQSrR5y1lQ6UminkRxUIizLae/VO+SOER4= Content-ID: <26031517EDDCA249A2979838F817F7D6@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: b6494417-d63f-4c3b-294e-08d73cef7d3f X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:38.2321 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 0Y/pPhbv8nguNJmQwilukjVd0lYDH3wXjJ8PIPTKTCWmJ6LtECiK1qOwbh3fIU6C9iD+22+mNieNRDJGZZbRlQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller These tracepoints decode HIF headers and provide more human readable results. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/bh.c | 5 + drivers/staging/wfx/traces.h | 211 +++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index 02a42e5c1e10..76afecdf579d 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -11,6 +11,7 @@ #include "bh.h" #include "wfx.h" #include "hwio.h" +#include "traces.h" #include "hif_api_cmd.h" static void device_wakeup(struct wfx_dev *wdev) @@ -67,6 +68,7 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) goto err; piggyback = le16_to_cpup((u16 *) (skb->data + alloc_len - 2)); + _trace_piggyback(piggyback, false); hif = (struct hif_msg *) skb->data; WARN(hif->encrypted & 0x1, "unsupported encryption type"); @@ -95,6 +97,7 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) if (!wdev->hif.tx_buffers_used) wake_up(&wdev->hif.tx_buffers_empty); } + _trace_hif_recv(hif, wdev->hif.tx_buffers_used); if (hif->id != HIF_IND_ID_EXCEPTION && hif->id != HIF_IND_ID_ERROR) { if (hif->seqnum != wdev->hif.rx_seqnum) @@ -171,6 +174,7 @@ static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif) goto end; wdev->hif.tx_buffers_used++; + _trace_hif_send(hif, wdev->hif.tx_buffers_used); end: if (is_encrypted) kfree(data); @@ -234,6 +238,7 @@ static void bh_work(struct work_struct *work) device_release(wdev); release_chip = true; } + _trace_bh_stats(stats_ind, stats_req, stats_cnf, wdev->hif.tx_buffers_used, release_chip); } /* diff --git a/drivers/staging/wfx/traces.h b/drivers/staging/wfx/traces.h index ba97df821f1b..fd75c4c7e9c7 100644 --- a/drivers/staging/wfx/traces.h +++ b/drivers/staging/wfx/traces.h @@ -15,6 +15,8 @@ #include #include "bus.h" +#include "hif_api_cmd.h" +#include "hif_api_mib.h" #if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) #define TRACE_DEFINE_ENUM(a) @@ -50,6 +52,167 @@ * #define list_for_print_symbolic list_names { -1, NULL } */ +#define _hif_msg_list \ + hif_cnf_name(ADD_KEY) \ + hif_cnf_name(BEACON_TRANSMIT) \ + hif_cnf_name(EDCA_QUEUE_PARAMS) \ + hif_cnf_name(JOIN) \ + hif_cnf_name(MAP_LINK) \ + hif_cnf_name(READ_MIB) \ + hif_cnf_name(REMOVE_KEY) \ + hif_cnf_name(RESET) \ + hif_cnf_name(SET_BSS_PARAMS) \ + hif_cnf_name(SET_PM_MODE) \ + hif_cnf_name(START) \ + hif_cnf_name(START_SCAN) \ + hif_cnf_name(STOP_SCAN) \ + hif_cnf_name(TX) \ + hif_cnf_name(MULTI_TRANSMIT) \ + hif_cnf_name(UPDATE_IE) \ + hif_cnf_name(WRITE_MIB) \ + hif_cnf_name(CONFIGURATION) \ + hif_cnf_name(CONTROL_GPIO) \ + hif_cnf_name(PREVENT_ROLLBACK) \ + hif_cnf_name(SET_SL_MAC_KEY) \ + hif_cnf_name(SL_CONFIGURE) \ + hif_cnf_name(SL_EXCHANGE_PUB_KEYS) \ + hif_cnf_name(SHUT_DOWN) \ + hif_ind_name(EVENT) \ + hif_ind_name(JOIN_COMPLETE) \ + hif_ind_name(RX) \ + hif_ind_name(SCAN_CMPL) \ + hif_ind_name(SET_PM_MODE_CMPL) \ + hif_ind_name(SUSPEND_RESUME_TX) \ + hif_ind_name(SL_EXCHANGE_PUB_KEYS) \ + hif_ind_name(ERROR) \ + hif_ind_name(EXCEPTION) \ + hif_ind_name(GENERIC) \ + hif_ind_name(WAKEUP) \ + hif_ind_name(STARTUP) + +#define hif_msg_list_enum _hif_msg_list + +#undef hif_cnf_name +#undef hif_ind_name +#define hif_cnf_name(msg) TRACE_DEFINE_ENUM(HIF_CNF_ID_##msg); +#define hif_ind_name(msg) TRACE_DEFINE_ENUM(HIF_IND_ID_##msg); +hif_msg_list_enum +#undef hif_cnf_name +#undef hif_ind_name +#define hif_cnf_name(msg) { HIF_CNF_ID_##msg, #msg }, +#define hif_ind_name(msg) { HIF_IND_ID_##msg, #msg }, +#define hif_msg_list hif_msg_list_enum { -1, NULL } + +#define _hif_mib_list \ + hif_mib_name(ARP_IP_ADDRESSES_TABLE) \ + hif_mib_name(ARP_KEEP_ALIVE_PERIOD) \ + hif_mib_name(BEACON_FILTER_ENABLE) \ + hif_mib_name(BEACON_FILTER_TABLE) \ + hif_mib_name(BEACON_WAKEUP_PERIOD) \ + hif_mib_name(BLOCK_ACK_POLICY) \ + hif_mib_name(CONFIG_DATA_FILTER) \ + hif_mib_name(COUNTERS_TABLE) \ + hif_mib_name(CURRENT_TX_POWER_LEVEL) \ + hif_mib_name(DOT11_MAC_ADDRESS) \ + hif_mib_name(DOT11_MAX_RECEIVE_LIFETIME) \ + hif_mib_name(DOT11_MAX_TRANSMIT_MSDU_LIFETIME) \ + hif_mib_name(DOT11_RTS_THRESHOLD) \ + hif_mib_name(DOT11_WEP_DEFAULT_KEY_ID) \ + hif_mib_name(GL_BLOCK_ACK_INFO) \ + hif_mib_name(GL_OPERATIONAL_POWER_MODE) \ + hif_mib_name(GL_SET_MULTI_MSG) \ + hif_mib_name(INACTIVITY_TIMER) \ + hif_mib_name(INTERFACE_PROTECTION) \ + hif_mib_name(IPV4_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(IPV6_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(KEEP_ALIVE_PERIOD) \ + hif_mib_name(MAC_ADDR_DATAFRAME_CONDITION) \ + hif_mib_name(NON_ERP_PROTECTION) \ + hif_mib_name(NS_IP_ADDRESSES_TABLE) \ + hif_mib_name(OVERRIDE_INTERNAL_TX_RATE) \ + hif_mib_name(PROTECTED_MGMT_POLICY) \ + hif_mib_name(RX_FILTER) \ + hif_mib_name(RCPI_RSSI_THRESHOLD) \ + hif_mib_name(SET_ASSOCIATION_MODE) \ + hif_mib_name(SET_DATA_FILTERING) \ + hif_mib_name(ETHERTYPE_DATAFRAME_CONDITION) \ + hif_mib_name(SET_HT_PROTECTION) \ + hif_mib_name(MAGIC_DATAFRAME_CONDITION) \ + hif_mib_name(SET_TX_RATE_RETRY_POLICY) \ + hif_mib_name(SET_UAPSD_INFORMATION) \ + hif_mib_name(PORT_DATAFRAME_CONDITION) \ + hif_mib_name(SLOT_TIME) \ + hif_mib_name(STATISTICS_TABLE) \ + hif_mib_name(TEMPLATE_FRAME) \ + hif_mib_name(TSF_COUNTER) \ + hif_mib_name(UC_MC_BC_DATAFRAME_CONDITION) + +#define hif_mib_list_enum _hif_mib_list + +#undef hif_mib_name +#define hif_mib_name(mib) TRACE_DEFINE_ENUM(HIF_MIB_ID_##mib); +hif_mib_list_enum +#undef hif_mib_name +#define hif_mib_name(mib) { HIF_MIB_ID_##mib, #mib }, +#define hif_mib_list hif_mib_list_enum { -1, NULL } + +DECLARE_EVENT_CLASS(hif_data, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv), + TP_STRUCT__entry( + __field(int, tx_fill_level) + __field(int, msg_id) + __field(const char *, msg_type) + __field(int, msg_len) + __field(int, buf_len) + __field(int, if_id) + __field(int, mib) + __array(u8, buf, 128) + ), + TP_fast_assign( + int header_len; + + __entry->tx_fill_level = tx_fill_level; + __entry->msg_len = hif->len; + __entry->msg_id = hif->id; + __entry->if_id = hif->interface; + if (is_recv) + __entry->msg_type = __entry->msg_id & 0x80 ? "IND" : "CNF"; + else + __entry->msg_type = "REQ"; + if (!is_recv && + (__entry->msg_id == HIF_REQ_ID_READ_MIB || __entry->msg_id == HIF_REQ_ID_WRITE_MIB)) { + __entry->mib = le16_to_cpup((u16 *) hif->body); + header_len = 4; + } else { + __entry->mib = -1; + header_len = 0; + } + __entry->buf_len = min_t(int, __entry->msg_len, sizeof(__entry->buf)) + - sizeof(struct hif_msg) - header_len; + memcpy(__entry->buf, hif->body + header_len, __entry->buf_len); + ), + TP_printk("%d:%d:%s_%s%s%s: %s%s (%d bytes)", + __entry->tx_fill_level, + __entry->if_id, + __print_symbolic(__entry->msg_id, hif_msg_list), + __entry->msg_type, + __entry->mib != -1 ? "/" : "", + __entry->mib != -1 ? __print_symbolic(__entry->mib, hif_mib_list) : "", + __print_hex(__entry->buf, __entry->buf_len), + __entry->msg_len > sizeof(__entry->buf) ? " ..." : "", + __entry->msg_len + ) +); +DEFINE_EVENT(hif_data, hif_send, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv)); +#define _trace_hif_send(hif, tx_fill_level) trace_hif_send(hif, tx_fill_level, false) +DEFINE_EVENT(hif_data, hif_recv, + TP_PROTO(struct hif_msg *hif, int tx_fill_level, bool is_recv), + TP_ARGS(hif, tx_fill_level, is_recv)); +#define _trace_hif_recv(hif, tx_fill_level) trace_hif_recv(hif, tx_fill_level, true) + #define wfx_reg_list_enum \ wfx_reg_name(WFX_REG_CONFIG, "CONFIG") \ wfx_reg_name(WFX_REG_CONTROL, "CONTROL") \ @@ -143,6 +306,54 @@ DEFINE_EVENT(io_data32, io_read32, #define _trace_io_ind_read32(reg, addr, val) trace_io_read32(reg, addr, val) #define _trace_io_read32(reg, val) trace_io_read32(reg, -1, val) +DECLARE_EVENT_CLASS(piggyback, + TP_PROTO(u32 val, bool ignored), + TP_ARGS(val, ignored), + TP_STRUCT__entry( + __field(int, val) + __field(bool, ignored) + ), + TP_fast_assign( + __entry->val = val; + __entry->ignored = ignored; + ), + TP_printk("CONTROL: %08x%s", + __entry->val, + __entry->ignored ? " (ignored)" : "" + ) +); +DEFINE_EVENT(piggyback, piggyback, + TP_PROTO(u32 val, bool ignored), + TP_ARGS(val, ignored)); +#define _trace_piggyback(val, ignored) trace_piggyback(val, ignored) + +TRACE_EVENT(bh_stats, + TP_PROTO(int ind, int req, int cnf, int busy, bool release), + TP_ARGS(ind, req, cnf, busy, release), + TP_STRUCT__entry( + __field(int, ind) + __field(int, req) + __field(int, cnf) + __field(int, busy) + __field(bool, release) + ), + TP_fast_assign( + __entry->ind = ind; + __entry->req = req; + __entry->cnf = cnf; + __entry->busy = busy; + __entry->release = release; + ), + TP_printk("IND/REQ/CNF:%3d/%3d/%3d, REQ in progress:%3d, WUP: %s", + __entry->ind, + __entry->req, + __entry->cnf, + __entry->busy, + __entry->release ? "release" : "keep" + ) +); +#define _trace_bh_stats(ind, req, cnf, busy, release) trace_bh_stats(ind, req, cnf, busy, release) + #endif /* This part must be outside protection */ From patchwork Thu Sep 19 10:52:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152159 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BAF0016B1 for ; Thu, 19 Sep 2019 10:54:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8528721924 for ; Thu, 19 Sep 2019 10:54:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="VKBdfLUJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389517AbfISKwy (ORCPT ); Thu, 19 Sep 2019 06:52:54 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389476AbfISKww (ORCPT ); Thu, 19 Sep 2019 06:52:52 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eME8hU/7hDNNOrHiqwbrOxUX91RbZYyd7YW71AfMg+ogWtO0iboRtl4fPuTfTN+ixzvE/OkL7b4roqxKYTyyv+R/uisG/QL/miuU/jyF+Yc2b1YlWD8uMvvzMFyI3Ov5PLSnQosI4CD6b3UNnQ1Q7W65vM+/tCtrXXq7xyMia36zXDJHA+ymeMbkUnklrIIxJdM0HUUa3ugS+uHZK0Xc4MYLhb0PV2ZkjofJd71wDgbb6XAd55Fab+4PqY0N6Xmfuv5EoG3g3QQW+beEiflZj05ZHFcx92AADzprzTiMKIwC6PSHUjBfrR9prwASOSIfi/UAtc5Z9Ota+4DArvFgTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=mjdyvvRksTlhVIpIRffIMgZsQ42ACLLqlz1hRapUSeA=; b=Rc/kjxn1fObTTdSyJZYuc6Nw2obgCsc0dSzRsl18UgWNVEv3a6NENrYDy6uk7nNO5VlI+/evlZsFq5vmBLPCcezSY84XTRE10D+xQs1rxqslZ2aqrH3EL1DCB3uMmSLaWqvyjR8rtu+KRqU9S+jt1xwAv0x1TcRm7qeaYDoLCus6tLOSNQwsQ49LzZTrBRR/kprm/CGwJSunU/IU934f3aQJXFfaIrGqKIcx1O/QIJrgZwK/SNYKLwvACscHVOW31z2PS5fDbFeNDKNYgjIjg/F0JcsSUge/eWvA0NDwcV4bZFqLXXzCxpKE1BjcULdYXkxykJoi05+zw2knZbqb7g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=mjdyvvRksTlhVIpIRffIMgZsQ42ACLLqlz1hRapUSeA=; b=VKBdfLUJaIXoYtChUbcbTVpGM1njsLnorniwtRnk8lVPAtQL1I6/pmwYhKGLWDaroqUSSPe31sNHs1bToQYTeE8Z5hvYSJsh43nvjFbxWDlQcXvlrYW8dOCm4sXGU+BDSIe8T4eZ5TAePQUhjYmdeEN7Zsb9sEQAy10xG9gbyeI= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:40 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:40 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 09/20] staging: wfx: add support for start-up indication Thread-Topic: [PATCH 09/20] staging: wfx: add support for start-up indication Thread-Index: AQHVbthaOUR9duoo6Ui+9y2oL2f6dw== Date: Thu, 19 Sep 2019 10:52:38 +0000 Message-ID: <20190919105153.15285-10-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: a5a2e0e7-320a-4769-a332-08d73cef7db7 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:3631; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: pbmfUeJ/ei+6feRjKWgiAeptR+blnHMl9eWZuu1WlhxxByu3uASJ68dNFVDVIZVjQL44sx+u61F7C6z0LWvCKTf/aymG5proZAiYu1qrp+XS269wOsUsTIbFY0OTtRSHRkrCpNLzp5XeexoYE+cVfdaKsl2cz5MqWwb0w2PZeHCbt/kZv1YvQtoMfIpbHcyjpXWAx0V1aCdWS9ucAMYo0OV4gpMId3YbR/DsblT9XCuiG736ot09NTpZdh6myPhA34Mj5ePU4hXDswOnY4Zy6FWVKM/WtuTmWVer4S2QTp6uDz8Ze3p6hIwJmyxvQHYC8E8pOHEvcQZqkJCh3+Xbx2QovUlTz/Bqb4O7orf0+f4gYI4pnL6UadupZELtimcfek9IFnkUGatGS7s6t+nqYIiuMbpPt7UhVXJZkAbjn+I= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: a5a2e0e7-320a-4769-a332-08d73cef7db7 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:38.6719 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: lKS7rhfgCwuAaTMHRuoKxr+I2eW/yWeWwmY1YcmPhCShGrr7dEsxFbMYAisMM1I91fZONMZJZYuk+KLnT8G40w== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Once firmware is loaded, it send a first indication to host. This indication signalize that host can start to communicate with firmware. In add, it contains information about chip and firmware (MAC addresses, firmware version, etc...). Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/bh.c | 4 ++- drivers/staging/wfx/hif_rx.c | 57 ++++++++++++++++++++++++++++++++++++ drivers/staging/wfx/hif_rx.h | 18 ++++++++++++ drivers/staging/wfx/main.c | 45 ++++++++++++++++++++++++++++ drivers/staging/wfx/wfx.h | 5 ++++ 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/wfx/hif_rx.c create mode 100644 drivers/staging/wfx/hif_rx.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 1abd3115f11d..35670b86c64f 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -7,6 +7,7 @@ wfx-y := \ bh.o \ hwio.o \ fwio.o \ + hif_rx.o \ main.o \ debug.o wfx-$(CONFIG_SPI) += bus_spi.o diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index 76afecdf579d..c40da3f1f25d 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -12,6 +12,7 @@ #include "wfx.h" #include "hwio.h" #include "traces.h" +#include "hif_rx.h" #include "hif_api_cmd.h" static void device_wakeup(struct wfx_dev *wdev) @@ -107,7 +108,8 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) } skb_put(skb, hif->len); - dev_kfree_skb(skb); /* FIXME: handle received data */ + // wfx_handle_rx takes care on SKB livetime + wfx_handle_rx(wdev, skb); return piggyback; diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c new file mode 100644 index 000000000000..5c207e6d4348 --- /dev/null +++ b/drivers/staging/wfx/hif_rx.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac + * (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "hif_rx.h" +#include "wfx.h" +#include "hif_api_cmd.h" + +static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_startup *body = buf; + + if (body->status || body->firmware_type > 4) { + dev_err(wdev->dev, "received invalid startup indication"); + return -EINVAL; + } + memcpy(&wdev->hw_caps, body, sizeof(struct hif_ind_startup)); + le32_to_cpus(&wdev->hw_caps.status); + le16_to_cpus(&wdev->hw_caps.hardware_id); + le16_to_cpus(&wdev->hw_caps.num_inp_ch_bufs); + le16_to_cpus(&wdev->hw_caps.size_inp_ch_buf); + + complete(&wdev->firmware_ready); + return 0; +} + +static const struct { + int msg_id; + int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); +} hif_handlers[] = { + { HIF_IND_ID_STARTUP, hif_startup_indication }, +}; + +void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) +{ + int i; + struct hif_msg *hif = (struct hif_msg *) skb->data; + int hif_id = hif->id; + + for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) { + if (hif_handlers[i].msg_id == hif_id) { + if (hif_handlers[i].handler) + hif_handlers[i].handler(wdev, hif, hif->body); + goto free; + } + } + dev_err(wdev->dev, "unsupported HIF ID %02x\n", hif_id); +free: + dev_kfree_skb(skb); +} diff --git a/drivers/staging/wfx/hif_rx.h b/drivers/staging/wfx/hif_rx.h new file mode 100644 index 000000000000..f07c10c8c6bd --- /dev/null +++ b/drivers/staging/wfx/hif_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of chip-to-host event (aka indications) of WFxxx Split Mac + * (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_RX_H +#define WFX_HIF_RX_H + +struct wfx_dev; +struct sk_buff; + +void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb); + +#endif diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index f0bea053a0d9..5e7e7225f068 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -12,6 +12,7 @@ */ #include #include +#include #include #include #include @@ -87,6 +88,9 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->hwbus_ops = hwbus_ops; wdev->hwbus_priv = hwbus_priv; memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + + init_completion(&wdev->firmware_ready); + return wdev; } @@ -96,7 +100,9 @@ void wfx_free_common(struct wfx_dev *wdev) int wfx_probe(struct wfx_dev *wdev) { + int i; int err; + const void *macaddr; wfx_bh_register(wdev); @@ -104,6 +110,45 @@ int wfx_probe(struct wfx_dev *wdev) if (err) goto err1; + err = wait_for_completion_interruptible_timeout(&wdev->firmware_ready, 10 * HZ); + if (err <= 0) { + if (err == 0) { + dev_err(wdev->dev, "timeout while waiting for startup indication. IRQ configuration error?\n"); + err = -ETIMEDOUT; + } else if (err == -ERESTARTSYS) { + dev_info(wdev->dev, "probe interrupted by user\n"); + } + goto err1; + } + + // FIXME: fill wiphy::hw_version + dev_info(wdev->dev, "started firmware %d.%d.%d \"%s\" (API: %d.%d, keyset: %02X, caps: 0x%.8X)\n", + wdev->hw_caps.firmware_major, wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build, wdev->hw_caps.firmware_label, + wdev->hw_caps.api_version_major, wdev->hw_caps.api_version_minor, + wdev->keyset, *((u32 *) &wdev->hw_caps.capabilities)); + + if (wfx_api_older_than(wdev, 1, 0)) { + dev_err(wdev->dev, "unsupported firmware API version (expect 1 while firmware returns %d)\n", + wdev->hw_caps.api_version_major); + err = -ENOTSUPP; + goto err1; + } + + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { + eth_zero_addr(wdev->addresses[i].addr); + macaddr = of_get_mac_address(wdev->dev->of_node); + if (macaddr) { + ether_addr_copy(wdev->addresses[i].addr, macaddr); + wdev->addresses[i].addr[ETH_ALEN - 1] += i; + } + ether_addr_copy(wdev->addresses[i].addr, wdev->hw_caps.mac_addr[i]); + if (!is_valid_ether_addr(wdev->addresses[i].addr)) { + dev_warn(wdev->dev, "using random MAC address\n"); + eth_random_addr(wdev->addresses[i].addr); + } + dev_info(wdev->dev, "MAC address %d: %pM\n", i, wdev->addresses[i].addr); + } return 0; diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 4f28938fa3a6..f5f9a337d828 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -10,6 +10,9 @@ #ifndef WFX_H #define WFX_H +#include +#include + #include "bh.h" #include "main.h" #include "hif_api_general.h" @@ -19,10 +22,12 @@ struct hwbus_ops; struct wfx_dev { struct wfx_platform_data pdata; struct device *dev; + struct mac_address addresses[2]; const struct hwbus_ops *hwbus_ops; void *hwbus_priv; u8 keyset; + struct completion firmware_ready; struct hif_ind_startup hw_caps; struct wfx_hif hif; }; From patchwork Thu Sep 19 10:52:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152135 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A8DB314DB for ; Thu, 19 Sep 2019 10:52:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7D41021924 for ; Thu, 19 Sep 2019 10:52:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="PZM82LLc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389437AbfISKws (ORCPT ); Thu, 19 Sep 2019 06:52:48 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389413AbfISKwp (ORCPT ); Thu, 19 Sep 2019 06:52:45 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=L/86TdKzPXoqGoaxwoS8lXFnpl5hMY7qwHNxbY/C2Yw3eE4fHCdkk+kvVM0eJspzrIEl2DFEKgcw5ATxyyT+jXvpzYVSensRHyMwkPccXHo9kGeATTfA2w+ttPLEBvKeSb+l3OMm52SwZA9AF5iURBiCme50w/QOgpwYYpN/gEVygKg5uuY9dCR9mVmoAGslwBXwKOqv1zZQxdo2fFTsUjx6Aym4WWploFoaQ4qax81K0j9uNR2bRESjGUt+1BGeCwU+YbLleDtOPM0EnB+jj4cWAVp2LYPI/DpCKUfuged5RTaQm6yyGdLlgd9Uac/TDY2REeAQ+WB+3+P2kgKxeQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=RzgzJ2vGlgg9gz0VCTcZn3NVg+4ji2QAhdRHuUkOvS0=; b=W1tCRlYLszNa/Bv+wHl6/PVMzGnRfreejEiFUIYVDd3iVoyJe2LvV8HbnMpsc2fQorwv0SywMKnxOn4YCcSjXkG9xRmDOg4N5aD7X8hDqaXDTVbweBly5YZ6Wd2SHjzwLvI6/1sAKhKba0mlv9fGZdGnfn7rRtpAatGVSozfRyutCULwoPsCKD6tt26D7b7v4ehbK5getOH63aywM0KpsVrrAQNsF/NvH+DO4eVgYMthsdLH98hPENuYWeGxRilJOkyTaJ1q75dbWVccQb3b3NJtCpkCNK+8NCbPj5FgTmQYFgiRs8hKhyI8iMBY5vtw7M2O+qiU0rBOGeug5w3vIw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=RzgzJ2vGlgg9gz0VCTcZn3NVg+4ji2QAhdRHuUkOvS0=; b=PZM82LLceXlv1uajKyqy7ciql8DfgiVOVnOYarl8l+kuX7BmyFmaAz0prgwzA19RxT2n15g6G72y2NQwUxGMZptYtaahQB+rRgFMDh7x8+5thgpfidE7/0rs+TVUECYq8Oz9r/fOSKbbGqP/S2I0epajvEl3bY79Ce109BQnrec= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:40 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:40 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 10/20] staging: wfx: instantiate mac80211 data Thread-Topic: [PATCH 10/20] staging: wfx: instantiate mac80211 data Thread-Index: AQHVbthaie3pdyJ79UymTTaSc7Pj9A== Date: Thu, 19 Sep 2019 10:52:39 +0000 Message-ID: <20190919105153.15285-11-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8a62c635-1713-48e1-5c83-08d73cef7e48 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:475; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(5660300002)(54906003)(6436002)(71200400001)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: aB9cX0EHCmHO1gp294XxESRjWflNIzW1v+dL0nkuqXAdefS6MfxapOdkJ9L3hQkV1iBMT9Chv4aGoM6z+tt/Oy5cYM03g+63fFpV2ReYhcoVEcudVsd1sqyaySmnh6OdTkeQXNUi+sWjoP65+DWP0PVfXOCYiIr4tduKzQi78dM644Lg276TsLDf52jRnIaTpy6YkYZrw9C0lRaFh/0agz5jb39f9tTRJydHrop+YxJs1/xFIqa4e4Cfc4zZMRnt/hyD1QOn+3jCNP0aCcKDXP5at2A+BnxeLex5ucAl1PFQR9CNGlx2izP9S1pZX1IkIIC7+HJoFRyveAvx0nXG44Uct5qMZ3jkzrion/hYe0W6qb3TwFjoVwL58qGTYrBvwTraLGApAh6LrSeg4Zd85rlAKtRctYfGR7eN/qJMs6M= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8a62c635-1713-48e1-5c83-08d73cef7e48 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:39.3775 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: qSXZ5B1lVdmsvJR2SCThGrBT3rUQJS8gvImRUOIW8aS/Cz9HwNSgpaHAJUc8r2RYkGUmsh8tISXZD2cl0gRBAg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Allocate a struct ieee80211_hw but do not yet register it. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/debug.c | 12 ++++++++++ drivers/staging/wfx/debug.h | 15 ++++++++++++ drivers/staging/wfx/main.c | 41 ++++++++++++++++++++++++++++++-- drivers/staging/wfx/sta.c | 46 ++++++++++++++++++++++++++++++++++++ drivers/staging/wfx/sta.h | 24 +++++++++++++++++++ drivers/staging/wfx/wfx.h | 8 +++++++ 7 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/debug.h create mode 100644 drivers/staging/wfx/sta.c create mode 100644 drivers/staging/wfx/sta.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 35670b86c64f..2896a2127c88 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -9,6 +9,7 @@ wfx-y := \ fwio.o \ hif_rx.o \ main.o \ + sta.o \ debug.o wfx-$(CONFIG_SPI) += bus_spi.o wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index bf44c944640d..f28c94d8de89 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -5,6 +5,18 @@ * Copyright (c) 2017-2019, Silicon Laboratories, Inc. * Copyright (c) 2010, ST-Ericsson */ +#include + +#include "wfx.h" #define CREATE_TRACE_POINTS #include "traces.h" + +int wfx_debug_init(struct wfx_dev *wdev) +{ + struct dentry *d; + + d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + + return 0; +} diff --git a/drivers/staging/wfx/debug.h b/drivers/staging/wfx/debug.h new file mode 100644 index 000000000000..8bfba1a9fa20 --- /dev/null +++ b/drivers/staging/wfx/debug.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Debugfs interface. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2011, ST-Ericsson + */ +#ifndef WFX_DEBUG_H +#define WFX_DEBUG_H + +struct wfx_dev; + +int wfx_debug_init(struct wfx_dev *wdev); + +#endif /* WFX_DEBUG_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 5e7e7225f068..ca0ca873bd7d 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -25,6 +25,9 @@ #include "hwio.h" #include "bus.h" #include "bh.h" +#include "sta.h" +#include "debug.h" +#include "hif_api_cmd.h" #include "wfx_version.h" MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); @@ -36,6 +39,13 @@ static int gpio_wakeup = -2; module_param(gpio_wakeup, int, 0644); MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none."); +static const struct ieee80211_ops wfx_ops = { + .start = wfx_start, + .stop = wfx_stop, + .add_interface = wfx_add_interface, + .remove_interface = wfx_remove_interface, +}; + bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) { if (wdev->hw_caps.api_version_major < major) @@ -79,11 +89,26 @@ struct wfx_dev *wfx_init_common(struct device *dev, const struct hwbus_ops *hwbus_ops, void *hwbus_priv) { + struct ieee80211_hw *hw; struct wfx_dev *wdev; - wdev = devm_kmalloc(dev, sizeof(*wdev), GFP_KERNEL); - if (!wdev) + hw = ieee80211_alloc_hw(sizeof(struct wfx_dev), &wfx_ops); + if (!hw) return NULL; + + SET_IEEE80211_DEV(hw, dev); + + hw->vif_data_size = sizeof(struct wfx_vif); + hw->sta_data_size = sizeof(struct wfx_sta_priv); + hw->queues = 4; + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg) + + sizeof(struct hif_req_tx) + + 4 /* alignment */ + 8 /* TKIP IV */; + + wdev = hw->priv; + wdev->hw = hw; wdev->dev = dev; wdev->hwbus_ops = hwbus_ops; wdev->hwbus_priv = hwbus_priv; @@ -96,6 +121,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, void wfx_free_common(struct wfx_dev *wdev) { + ieee80211_free_hw(wdev->hw); } int wfx_probe(struct wfx_dev *wdev) @@ -127,6 +153,11 @@ int wfx_probe(struct wfx_dev *wdev) wdev->hw_caps.firmware_build, wdev->hw_caps.firmware_label, wdev->hw_caps.api_version_major, wdev->hw_caps.api_version_minor, wdev->keyset, *((u32 *) &wdev->hw_caps.capabilities)); + snprintf(wdev->hw->wiphy->fw_version, sizeof(wdev->hw->wiphy->fw_version), + "%d.%d.%d", + wdev->hw_caps.firmware_major, + wdev->hw_caps.firmware_minor, + wdev->hw_caps.firmware_build); if (wfx_api_older_than(wdev, 1, 0)) { dev_err(wdev->dev, "unsupported firmware API version (expect 1 while firmware returns %d)\n", @@ -150,8 +181,14 @@ int wfx_probe(struct wfx_dev *wdev) dev_info(wdev->dev, "MAC address %d: %pM\n", i, wdev->addresses[i].addr); } + err = wfx_debug_init(wdev); + if (err) + goto err2; + return 0; +err2: + ieee80211_free_hw(wdev->hw); err1: wfx_bh_unregister(wdev); return err; diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c new file mode 100644 index 000000000000..fe3ff6536a87 --- /dev/null +++ b/drivers/staging/wfx/sta.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include + +#include "sta.h" +#include "wfx.h" + +int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + int i; + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + if (!wdev->vif[i]) { + wdev->vif[i] = vif; + wvif->id = i; + break; + } + } + if (i == ARRAY_SIZE(wdev->vif)) + return -EOPNOTSUPP; + wvif->vif = vif; + wvif->wdev = wdev; + + return 0; +} + +void wfx_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ +} + +int wfx_start(struct ieee80211_hw *hw) +{ + return 0; +} + +void wfx_stop(struct ieee80211_hw *hw) +{ +} diff --git a/drivers/staging/wfx/sta.h b/drivers/staging/wfx/sta.h new file mode 100644 index 000000000000..f17b4d1511d7 --- /dev/null +++ b/drivers/staging/wfx/sta.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_STA_H +#define WFX_STA_H + +#include + +struct wfx_sta_priv { + int link_id; + int vif_id; +}; + +// mac80211 interface +int wfx_start(struct ieee80211_hw *hw); +void wfx_stop(struct ieee80211_hw *hw); +int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + +#endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index f5f9a337d828..a7e571e0da30 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -22,6 +22,8 @@ struct hwbus_ops; struct wfx_dev { struct wfx_platform_data pdata; struct device *dev; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif[2]; struct mac_address addresses[2]; const struct hwbus_ops *hwbus_ops; void *hwbus_priv; @@ -32,4 +34,10 @@ struct wfx_dev { struct wfx_hif hif; }; +struct wfx_vif { + struct wfx_dev *wdev; + struct ieee80211_vif *vif; + int id; +}; + #endif /* WFX_H */ From patchwork Thu Sep 19 10:52:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152153 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CFDAB14DB for ; Thu, 19 Sep 2019 10:54:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8F07D217D6 for ; Thu, 19 Sep 2019 10:54:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="TArnjKxe" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389539AbfISKw4 (ORCPT ); Thu, 19 Sep 2019 06:52:56 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389495AbfISKwz (ORCPT ); Thu, 19 Sep 2019 06:52:55 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=e6wetPxFl3vONVb/g5stZ+im3nkJB1SHbQNq6kZzUFcfyQ5tVGesGS2ueRF8Lqp/qJnDqh4fSAtFG4MOy1IW89HvdWUAAT+/Ih03FRWBr2HxCPe8X+zGzZscr8/ci1P8U8iMu/S9CCqhe60kUU/hUVmpqKr0+lUJrwBcOXkzNxn16qhUlhYmoXbKyU1yODk9dbra5sHowJvBLnAHHWKVy852lmutGDNUw53BBCvftnkFmRriOjXrDIA0n1nO59UJaz688oQhiZFpwjt/0zUOOtPFVKtzSuCVHxX5SI5XaPee+5hqi7bKzLjSClQpJ7q9ZYhsN56RhZBn6iNHS5T6qg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=vdL9KYE7hw1kkZQvNbREpyU5YHnYZt0e/MRBKP+5NrA=; b=P6Cxkc8TwwGOEGAqt5CUdxsPh19wPH9hUkc+Sprpw25E3s2BXCfus8lscfMXjK8FQ2ptqrOnwKspdo67eiUBhbHBOcwrq3ojgIRaRjMekkt9Au6uGu2GTViEwSTqGp6E84wv4zsiACYJGxH1LpB7yLOfcRW3a5xhBNRf2N1aOG5fMzjKj7z47uv9Oks08for9r72ynlyPo3XoXr2p/ay5W8OAS/4yNEk6gdQm5Y201Yu38HZVzPg3EjBz3e5r2rtkAMad7AcvQu/vIiSaT7DBjm5uzBUhYusK4cY0lj4MSBKb4xy2n6HdfccHxwNHPcikOxZFwFYHcgO4IY/fUTNPQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=vdL9KYE7hw1kkZQvNbREpyU5YHnYZt0e/MRBKP+5NrA=; b=TArnjKxeTDjmWEPJcK3PPB7yf780CmT/Z9XkQdG9X14HWr4ueAGRQoy6T7ePhpDYKDFK8CF2X8HwJa4LwjEOAvFZNL+sjqBXeostFjG2htN21m8le8kuXT0fBOVTGiuA9p7A4vDr7KaXoAMOZGvrehclmbkh8rVsOAEKEytP4KU= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:41 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:41 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 11/20] staging: wfx: allow to send commands to chip Thread-Topic: [PATCH 11/20] staging: wfx: allow to send commands to chip Thread-Index: AQHVbthbuB8XOakPRkGjbevJQDirEg== Date: Thu, 19 Sep 2019 10:52:39 +0000 Message-ID: <20190919105153.15285-12-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: ba005efa-d34f-4525-379e-08d73cef7ea7 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:39; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: V8ETSH/BoOBiL/nBQUww2OVB+b0ZvmzHpDo9rhze5/GFGpQZx/dyd1V4RHshs+6jB7ApHQ7BhE7d0ZyhpPCyxDd1L+7HYrrxkh/y1JVfBc41EOt7aHrr2xviFzMmydWfzNBhHP7i8b5Lz7khAa+si7k6aV71ThgfOuU+oDS2OgUY2olWiT0DVoVoViHx7mPuvnW7voOoEty81mWLdSb36RfSKYDHqhM1ZBta2SH7GrsqVBj/wNksTGjjXC2Gm6n6WW7wOsZwvNpnjG7+IWt7Aq+D4M3xGsdigc8G76nbu+4/VplDm71HAnuhN7aHamt5tulGJ3X4ML4alMPhOH/VKk5xmWhq8OTK8seHmetCjQcAUZQTqB5i4bb0Dhw/4+2EtfgYqGrcpX4J9uaX+ZHzP5bgPw1FIjHn28vwGf6g4Nw= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: ba005efa-d34f-4525-379e-08d73cef7ea7 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:39.9971 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: joZkkxWy5Qf1+rUBwfqJbV+z4qZhllfgVU1kSfA65dbc/rnfGewE6vPyC58SxBSrE7N/skmKVWyjHs+AG7aq4A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Chip has multiple input buffers and can handle multiple 802.11 frames in parallel. However, other HIF command must be sent sequentially. wsm_send_cmd() handles these requests. This commit also add send_hif_cmd in debugfs. This file allows to send arbitrary commands to chip. It can be used for debug and testing. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/bh.c | 5 +- drivers/staging/wfx/debug.c | 130 +++++++++++++++++++++++++++++++++++ drivers/staging/wfx/debug.h | 4 ++ drivers/staging/wfx/hif_rx.c | 45 ++++++++++++ drivers/staging/wfx/hif_tx.c | 87 +++++++++++++++++++++++ drivers/staging/wfx/hif_tx.h | 33 +++++++++ drivers/staging/wfx/main.c | 1 + drivers/staging/wfx/wfx.h | 4 ++ 9 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/wfx/hif_tx.c create mode 100644 drivers/staging/wfx/hif_tx.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 2896a2127c88..e158589468a3 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -7,6 +7,7 @@ wfx-y := \ bh.o \ hwio.o \ fwio.o \ + hif_tx.o \ hif_rx.o \ main.o \ sta.o \ diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index c40da3f1f25d..c94c9c401a69 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -190,7 +190,10 @@ static int bh_work_tx(struct wfx_dev *wdev, int max_msg) for (i = 0; i < max_msg; i++) { hif = NULL; if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) { - /* FIXME: get queued data */ + if (try_wait_for_completion(&wdev->hif_cmd.ready)) { + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); + hif = wdev->hif_cmd.buf_send; + } } if (!hif) return i; diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index f28c94d8de89..0a328c96eaa0 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -7,16 +7,146 @@ */ #include +#include "debug.h" #include "wfx.h" #define CREATE_TRACE_POINTS #include "traces.h" +static const struct trace_print_flags hif_msg_print_map[] = { + hif_msg_list, +}; + +static const struct trace_print_flags hif_mib_print_map[] = { + hif_mib_list, +}; + +static const struct trace_print_flags wfx_reg_print_map[] = { + wfx_reg_list, +}; + +static const char *get_symbol(unsigned long val, + const struct trace_print_flags *symbol_array) +{ + int i; + + for (i = 0; symbol_array[i].mask != -1; i++) { + if (val == symbol_array[i].mask) + return symbol_array[i].name; + } + + return "unknown"; +} + +const char *get_hif_name(unsigned long id) +{ + return get_symbol(id, hif_msg_print_map); +} + +const char *get_mib_name(unsigned long id) +{ + return get_symbol(id, hif_mib_print_map); +} + +const char *get_reg_name(unsigned long id) +{ + return get_symbol(id, wfx_reg_print_map); +} + +struct dbgfs_hif_msg { + struct wfx_dev *wdev; + struct completion complete; + u8 reply[1024]; + int ret; +}; + +static ssize_t wfx_send_hif_msg_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dbgfs_hif_msg *context = file->private_data; + struct wfx_dev *wdev = context->wdev; + struct hif_msg *request; + + if (completion_done(&context->complete)) { + dev_dbg(wdev->dev, "read previous result before start a new one\n"); + return -EBUSY; + } + if (count < sizeof(struct hif_msg)) + return -EINVAL; + + // wfx_cmd_send() chekc that reply buffer is wide enough, but do not + // return precise length read. User have to know how many bytes should + // be read. Filling reply buffer with a memory pattern may help user. + memset(context->reply, sizeof(context->reply), 0xFF); + request = memdup_user(user_buf, count); + if (IS_ERR(request)) + return PTR_ERR(request); + if (request->len != count) { + kfree(request); + return -EINVAL; + } + context->ret = wfx_cmd_send(wdev, request, context->reply, sizeof(context->reply), false); + + kfree(request); + complete(&context->complete); + return count; +} + +static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dbgfs_hif_msg *context = file->private_data; + int ret; + + if (count > sizeof(context->reply)) + return -EINVAL; + ret = wait_for_completion_interruptible(&context->complete); + if (ret) + return ret; + if (context->ret < 0) + return context->ret; + // Be carefull, write() is waiting for a full message while read() + // only return a payload + ret = copy_to_user(user_buf, context->reply, count); + if (ret) + return ret; + + return count; +} + +static int wfx_send_hif_msg_open(struct inode *inode, struct file *file) +{ + struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL); + + if (!context) + return -ENOMEM; + context->wdev = inode->i_private; + init_completion(&context->complete); + file->private_data = context; + return 0; +} + +static int wfx_send_hif_msg_release(struct inode *inode, struct file *file) +{ + struct dbgfs_hif_msg *context = file->private_data; + + kfree(context); + return 0; +} + +static const struct file_operations wfx_send_hif_msg_fops = { + .open = wfx_send_hif_msg_open, + .release = wfx_send_hif_msg_release, + .write = wfx_send_hif_msg_write, + .read = wfx_send_hif_msg_read, +}; + int wfx_debug_init(struct wfx_dev *wdev) { struct dentry *d; d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); return 0; } diff --git a/drivers/staging/wfx/debug.h b/drivers/staging/wfx/debug.h index 8bfba1a9fa20..6f2f84d64c9e 100644 --- a/drivers/staging/wfx/debug.h +++ b/drivers/staging/wfx/debug.h @@ -12,4 +12,8 @@ struct wfx_dev; int wfx_debug_init(struct wfx_dev *wdev); +const char *get_hif_name(unsigned long id); +const char *get_mib_name(unsigned long id); +const char *get_reg_name(unsigned long id); + #endif /* WFX_DEBUG_H */ diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index 5c207e6d4348..ba8ea4f3c91b 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -13,6 +13,43 @@ #include "wfx.h" #include "hif_api_cmd.h" +static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + // All confirm messages start with status + int status = le32_to_cpu(*((__le32 *) buf)); + int cmd = hif->id; + int len = hif->len - 4; // drop header + + WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); + + if (!wdev->hif_cmd.buf_send) { + dev_warn(wdev->dev, "unexpected confirmation: 0x%.2x\n", cmd); + return -EINVAL; + } + + if (cmd != wdev->hif_cmd.buf_send->id) { + dev_warn(wdev->dev, "chip response mismatch request: 0x%.2x vs 0x%.2x\n", + cmd, wdev->hif_cmd.buf_send->id); + return -EINVAL; + } + + if (wdev->hif_cmd.buf_recv) { + if (wdev->hif_cmd.len_recv >= len) + memcpy(wdev->hif_cmd.buf_recv, buf, len); + else + status = -ENOMEM; + } + wdev->hif_cmd.ret = status; + + if (!wdev->hif_cmd.async) { + complete(&wdev->hif_cmd.done); + } else { + wdev->hif_cmd.buf_send = NULL; + mutex_unlock(&wdev->hif_cmd.lock); + } + return status; +} + static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct hif_ind_startup *body = buf; @@ -44,6 +81,14 @@ void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) struct hif_msg *hif = (struct hif_msg *) skb->data; int hif_id = hif->id; + // Note: mutex_is_lock cause an implicit memory barrier that protect + // buf_send + if (mutex_is_locked(&wdev->hif_cmd.lock) + && wdev->hif_cmd.buf_send + && wdev->hif_cmd.buf_send->id == hif_id) { + hif_generic_confirm(wdev, hif, hif->body); + goto free; + } for (i = 0; i < ARRAY_SIZE(hif_handlers); i++) { if (hif_handlers[i].msg_id == hif_id) { if (hif_handlers[i].handler) diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c new file mode 100644 index 000000000000..f81a19089db4 --- /dev/null +++ b/drivers/staging/wfx/hif_tx.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx + * Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "hif_tx.h" +#include "wfx.h" +#include "bh.h" +#include "debug.h" + +void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) +{ + init_completion(&hif_cmd->ready); + init_completion(&hif_cmd->done); + mutex_init(&hif_cmd->lock); +} + +int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async) +{ + const char *mib_name = ""; + const char *mib_sep = ""; + int cmd = request->id; + int vif = request->interface; + int ret; + + WARN(wdev->hif_cmd.buf_recv && wdev->hif_cmd.async, "API usage error"); + + // Do not wait for any reply if chip is frozen + if (wdev->chip_frozen) + return -ETIMEDOUT; + + mutex_lock(&wdev->hif_cmd.lock); + WARN(wdev->hif_cmd.buf_send, "data locking error"); + + // Note: call to complete() below has an implicit memory barrier that + // hopefully protect buf_send + wdev->hif_cmd.buf_send = request; + wdev->hif_cmd.buf_recv = reply; + wdev->hif_cmd.len_recv = reply_len; + wdev->hif_cmd.async = async; + complete(&wdev->hif_cmd.ready); + + wfx_bh_request_tx(wdev); + + // NOTE: no timeout is catched async is enabled + if (async) + return 0; + + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 1 * HZ); + if (!ret) { + dev_err(wdev->dev, "chip is abnormally long to answer\n"); + reinit_completion(&wdev->hif_cmd.ready); + ret = wait_for_completion_timeout(&wdev->hif_cmd.done, 3 * HZ); + } + if (!ret) { + dev_err(wdev->dev, "chip did not answer\n"); + wdev->chip_frozen = 1; + reinit_completion(&wdev->hif_cmd.done); + ret = -ETIMEDOUT; + } else { + ret = wdev->hif_cmd.ret; + } + + wdev->hif_cmd.buf_send = NULL; + mutex_unlock(&wdev->hif_cmd.lock); + + if (ret && (cmd == HIF_REQ_ID_READ_MIB || cmd == HIF_REQ_ID_WRITE_MIB)) { + mib_name = get_mib_name(((u16 *) request)[2]); + mib_sep = "/"; + } + if (ret < 0) + dev_err(wdev->dev, + "WSM request %s%s%s (%#.2x) on vif %d returned error %d\n", + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + if (ret > 0) + dev_warn(wdev->dev, + "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n", + get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + + return ret; +} diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h new file mode 100644 index 000000000000..ccf2b7e5e851 --- /dev/null +++ b/drivers/staging/wfx/hif_tx.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of host-to-chip commands (aka request/confirmation) of WFxxx + * Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_TX_H +#define WFX_HIF_TX_H + +#include "hif_api_cmd.h" + +struct wfx_dev; +struct wfx_vif; + +struct wfx_hif_cmd { + struct mutex lock; + struct completion ready; + struct completion done; + bool async; + struct hif_msg *buf_send; + void *buf_recv; + size_t len_recv; + int ret; +}; + +void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd); +int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, + void *reply, size_t reply_len, bool async); + +#endif diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index ca0ca873bd7d..8973eeb60eb8 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -115,6 +115,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, memcpy(&wdev->pdata, pdata, sizeof(*pdata)); init_completion(&wdev->firmware_ready); + wfx_init_hif_cmd(&wdev->hif_cmd); return wdev; } diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index a7e571e0da30..bf9de11f8896 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -15,6 +15,7 @@ #include "bh.h" #include "main.h" +#include "hif_tx.h" #include "hif_api_general.h" struct hwbus_ops; @@ -32,6 +33,9 @@ struct wfx_dev { struct completion firmware_ready; struct hif_ind_startup hw_caps; struct wfx_hif hif; + int chip_frozen; + + struct wfx_hif_cmd hif_cmd; }; struct wfx_vif { From patchwork Thu Sep 19 10:52:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152155 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3D5BF14DB for ; Thu, 19 Sep 2019 10:54:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EB66E21929 for ; Thu, 19 Sep 2019 10:54:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="p+xHyGn9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389530AbfISKyX (ORCPT ); Thu, 19 Sep 2019 06:54:23 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389518AbfISKwz (ORCPT ); Thu, 19 Sep 2019 06:52:55 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=STp88yZfaSt/3sSrjkr72wOP/8h5CwEKrjQCfF+3KNJdUnyQj4XVOmEH6vbrxU+U1bvWrcVBR7G6Ihil/wOdsuKbTYWYeHOG25dMyxSxGtPfm+3/b80Xmepcs+GSND9+Rmn4+9Fqi+UdxOBv2U1yT1hAD9sEgGagwXB1+6VUhjlVp6btaPxrstOMs/NEB22qIhoC1PwO7S/XcysH0JGFXOt74YOaYwtCdb1j0BLM0MiJK1+lJaTD2i1gUpTz7yMfTOgZYRhzKsfB5Gx2ZxKQKZ91Kf+DyPKKYREd5mamAmF1Hx6xSo/Y6HxmRdtcoxE0Vv5ISsYsYkPsPuvBhTLUEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=UXtm+PL/ruBchth+jqURHASp9Wc7XDjMS8K7uJ2Als8=; b=Kx5OfyHkZJz7wlbylkuWPduWi4kdhWvhsA3CYcq4YX79qWfU8uKqJytTdmLz5MQobysFKy4YXkJS8WHXoNsH8YOOpSbKkL6oQlbwcWcUD80WlbG2LeJviOB2ytkXtQsarRL3WuEJxTm/6vhlTi/utSnNq2Jmb5zdXI9eyGhlB9wBR2TPhRZcyG1kS7pvTB7cc/+FKlb62UIUPz+6wNUZ2b6oiCDUnIowU85xodYx8J3aHJ1qCsV/tju5L5unpXkIyoYw8S8dYL0LmGsByoE/z1nEJUH7SukRCw8TBhSkh32nBAaVkuhUFYuBCwE2cO4fBbiNLneIEes3/q1TUXww8w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=UXtm+PL/ruBchth+jqURHASp9Wc7XDjMS8K7uJ2Als8=; b=p+xHyGn9hVPALdNRKQHOO+bvzsH4PwcRvH5EFPcg2O9nNNcA8Gl3UCh/w3DGAwPPCTOkTQd9nsvNbzDtkh1+Bt3GtAP5d6OIQmmcihTAHkYxWzyNF9q/y0V3/Fhjvky75R+IRYHw3V6hIgG/xqn/SboPohPYHhh7q/i+SHLPYCc= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:42 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:42 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 12/20] staging: wfx: add HIF commands helpers Thread-Topic: [PATCH 12/20] staging: wfx: add HIF commands helpers Thread-Index: AQHVbthbWN2utnxkSEed46CUrldewQ== Date: Thu, 19 Sep 2019 10:52:40 +0000 Message-ID: <20190919105153.15285-13-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: a060fd02-dc93-4888-ce34-08d73cef7ef8 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:17; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007)(579004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: gNcFv9MZ/lgoDcHIvz9tGng5N9XbXzLqASQbb0P502/wrIbdFdQ8vM9B3fr8CSYNQgnuIanvs1nAiZ+JGFx9CmQSS8vhHSx6sbjWxDVMFFgbLkVSu3ehtroPy3XPlYWiKCwykBy/L5JfwR7EGNsILH3vr2lzA4Df0VN4kIXNQfPRyqqjV8MuiPWWULokun0VTW3PQ6UmNHkOLxpvEMpoHS5aX3HqnoBl9E1O+5Stt6AezYO0/0t+0j7FVqDLK5UpZJhwzSOzxgEcNv474osEc7mBPGLa6TapDda2fP1/KD8Uk2F6UfYxFxnSYV+NCf6v0JeyKKR7RnEUipy58GzOtKtwanUbrE7bOhBO98z7WFtpVMQG0l/oUev9sJoN6oergtWDpdMickU+uBEr+YSzYdQlF5MHcz+3d+jYDJQ1O/4= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: a060fd02-dc93-4888-ce34-08d73cef7ef8 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:40.5598 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: TVOHQhj1XWg1JskYYFtiS6lr2x2ZGhWhfsY5A3CciuGCdd2QXgC4sKI7uT7gtY9ndLVarCWe98EeKBLxS7hW8w== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Provide an abstraction for HIF commands. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/hif_tx.c | 375 +++++++++++++++++++++++++++++++ drivers/staging/wfx/hif_tx.h | 33 +++ drivers/staging/wfx/hif_tx_mib.h | 281 +++++++++++++++++++++++ drivers/staging/wfx/wfx.h | 6 + 4 files changed, 695 insertions(+) create mode 100644 drivers/staging/wfx/hif_tx_mib.h diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c index f81a19089db4..781a6e28dbad 100644 --- a/drivers/staging/wfx/hif_tx.c +++ b/drivers/staging/wfx/hif_tx.c @@ -12,6 +12,7 @@ #include "hif_tx.h" #include "wfx.h" #include "bh.h" +#include "hwio.h" #include "debug.h" void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) @@ -21,6 +22,29 @@ void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) mutex_init(&hif_cmd->lock); } +static void wfx_fill_header(struct hif_msg *hif, int if_id, unsigned int cmd, size_t size) +{ + if (if_id == -1) + if_id = 2; + + WARN(cmd > 0x3f, "invalid WSM command %#.2x", cmd); + WARN(size > 0xFFF, "requested buffer is too large: %zu bytes", size); + WARN(if_id > 0x3, "invalid interface ID %d", if_id); + + hif->len = cpu_to_le16(size + 4); + hif->id = cmd; + hif->interface = if_id; +} + +static void *wfx_alloc_hif(size_t body_len, struct hif_msg **hif) +{ + *hif = kzalloc(sizeof(struct hif_msg) + body_len, GFP_KERNEL); + if (*hif) + return (*hif)->body; + else + return NULL; +} + int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async) { const char *mib_name = ""; @@ -85,3 +109,354 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz return ret; } + +// This function is special. After HIF_REQ_ID_SHUT_DOWN, chip won't reply to any +// request anymore. We need to slightly hack struct wfx_hif_cmd for that job. Be +// carefull to only call this funcion during device unregister. +int hif_shutdown(struct wfx_dev *wdev) +{ + int ret; + struct hif_msg *hif; + + wfx_alloc_hif(0, &hif); + wfx_fill_header(hif, -1, HIF_REQ_ID_SHUT_DOWN, 0); + ret = wfx_cmd_send(wdev, hif, NULL, 0, true); + // After this command, chip won't reply. Be sure to give enough time to + // bh to send buffer: + msleep(100); + wdev->hif_cmd.buf_send = NULL; + if (wdev->pdata.gpio_wakeup) + gpiod_set_value(wdev->pdata.gpio_wakeup, 0); + else + control_reg_write(wdev, 0); + mutex_unlock(&wdev->hif_cmd.lock); + kfree(hif); + return ret; +} + +int hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len) +{ + int ret; + size_t buf_len = sizeof(struct hif_req_configuration) + len; + struct hif_msg *hif; + struct hif_req_configuration *body = wfx_alloc_hif(buf_len, &hif); + + body->length = cpu_to_le16(len); + memcpy(body->pds_data, conf, len); + wfx_fill_header(hif, -1, HIF_REQ_ID_CONFIGURATION, buf_len); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_reset(struct wfx_vif *wvif, bool reset_stat) +{ + int ret; + struct hif_msg *hif; + struct hif_req_reset *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->reset_flags.reset_stat = reset_stat; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_RESET, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_cnf_read_mib) + val_len; + struct hif_req_read_mib *body = wfx_alloc_hif(sizeof(*body), &hif); + struct hif_cnf_read_mib *reply = kmalloc(buf_len, GFP_KERNEL); + + body->mib_id = cpu_to_le16(mib_id); + wfx_fill_header(hif, vif_id, HIF_REQ_ID_READ_MIB, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, reply, buf_len, false); + + if (!ret && mib_id != reply->mib_id) { + dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__); + ret = -EIO; + } + if (ret == -ENOMEM) + dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n", + get_mib_name(mib_id), val_len, reply->length); + if (!ret) + memcpy(val, &reply->mib_data, reply->length); + else + memset(val, 0xFF, val_len); + kfree(hif); + kfree(reply); + return ret; +} + +int hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, size_t val_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_req_write_mib) + val_len; + struct hif_req_write_mib *body = wfx_alloc_hif(buf_len, &hif); + + body->mib_id = cpu_to_le16(mib_id); + body->length = cpu_to_le16(val_len); + memcpy(&body->mib_data, val, val_len); + wfx_fill_header(hif, vif_id, HIF_REQ_ID_WRITE_MIB, buf_len); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_scan(struct wfx_vif *wvif, const struct wfx_scan_params *arg) +{ + int ret, i; + struct hif_msg *hif; + struct hif_ssid_def *ssids; + size_t buf_len = sizeof(struct hif_req_start_scan) + + arg->scan_req.num_of_channels * sizeof(u8) + + arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def); + struct hif_req_start_scan *body = wfx_alloc_hif(buf_len, &hif); + u8 *ptr = (u8 *) body + sizeof(*body); + + WARN(arg->scan_req.num_of_channels > HIF_API_MAX_NB_CHANNELS, "invalid params"); + WARN(arg->scan_req.num_of_ssi_ds > 2, "invalid params"); + WARN(arg->scan_req.band > 1, "invalid params"); + + // FIXME: This API is unnecessary complex, fixing NumOfChannels and + // adding a member SsidDef at end of struct hif_req_start_scan would + // simplify that a lot. + memcpy(body, &arg->scan_req, sizeof(*body)); + cpu_to_le32s(&body->min_channel_time); + cpu_to_le32s(&body->max_channel_time); + cpu_to_le32s(&body->tx_power_level); + memcpy(ptr, arg->ssids, arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def)); + ssids = (struct hif_ssid_def *) ptr; + for (i = 0; i < body->num_of_ssi_ds; ++i) + cpu_to_le32s(&ssids[i].ssid_length); + ptr += arg->scan_req.num_of_ssi_ds * sizeof(struct hif_ssid_def); + memcpy(ptr, arg->ch, arg->scan_req.num_of_channels * sizeof(u8)); + ptr += arg->scan_req.num_of_channels * sizeof(u8); + WARN(buf_len != ptr - (u8 *) body, "allocation size mismatch"); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START_SCAN, buf_len); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_stop_scan(struct wfx_vif *wvif) +{ + int ret; + struct hif_msg *hif; + // body associated to HIF_REQ_ID_STOP_SCAN is empty + wfx_alloc_hif(0, &hif); + + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_STOP_SCAN, 0); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_join(struct wfx_vif *wvif, const struct hif_req_join *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_join *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(struct hif_req_join)); + cpu_to_le16s(&body->channel_number); + cpu_to_le16s(&body->atim_window); + cpu_to_le32s(&body->ssid_length); + cpu_to_le32s(&body->beacon_interval); + cpu_to_le32s(&body->basic_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_JOIN, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_bss_params(struct wfx_vif *wvif, const struct hif_req_set_bss_params *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_bss_params *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->aid); + cpu_to_le32s(&body->operational_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_BSS_PARAMS, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_add_key(struct wfx_dev *wdev, const struct hif_req_add_key *arg) +{ + int ret; + struct hif_msg *hif; + // FIXME: only send necessary bits + struct hif_req_add_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + // FIXME: swap bytes as necessary in body + memcpy(body, arg, sizeof(*body)); + if (wfx_api_older_than(wdev, 1, 5)) + // Legacy firmwares expect that add_key to be sent on right + // interface. + wfx_fill_header(hif, arg->int_id, HIF_REQ_ID_ADD_KEY, sizeof(*body)); + else + wfx_fill_header(hif, -1, HIF_REQ_ID_ADD_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_remove_key(struct wfx_dev *wdev, int idx) +{ + int ret; + struct hif_msg *hif; + struct hif_req_remove_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->entry_index = idx; + wfx_fill_header(hif, -1, HIF_REQ_ID_REMOVE_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_edca_queue_params(struct wfx_vif *wvif, const struct hif_req_edca_queue_params *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_edca_queue_params *body = wfx_alloc_hif(sizeof(*body), &hif); + + // NOTE: queues numerotation are not the same between WFx and Linux + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->cw_min); + cpu_to_le16s(&body->cw_max); + cpu_to_le16s(&body->tx_op_limit); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_EDCA_QUEUE_PARAMS, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_pm_mode *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_SET_PM_MODE, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_start(struct wfx_vif *wvif, const struct hif_req_start *arg) +{ + int ret; + struct hif_msg *hif; + struct hif_req_start *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body, arg, sizeof(*body)); + cpu_to_le16s(&body->channel_number); + cpu_to_le32s(&body->beacon_interval); + cpu_to_le32s(&body->basic_rate_set); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_START, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_beacon_transmit(struct wfx_vif *wvif, bool enable_beaconing) +{ + int ret; + struct hif_msg *hif; + struct hif_req_beacon_transmit *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->enable_beaconing = enable_beaconing ? 1 : 0; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_BEACON_TRANSMIT, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_map_link(struct wfx_vif *wvif, u8 *mac_addr, int flags, int sta_id) +{ + int ret; + struct hif_msg *hif; + struct hif_req_map_link *body = wfx_alloc_hif(sizeof(*body), &hif); + + if (mac_addr) + ether_addr_copy(body->mac_addr, mac_addr); + body->map_link_flags = *(struct hif_map_link_flags *) &flags; + body->peer_sta_id = sta_id; + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_MAP_LINK, sizeof(*body)); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_update_ie(struct wfx_vif *wvif, const struct hif_ie_flags *target_frame, + const u8 *ies, size_t ies_len) +{ + int ret; + struct hif_msg *hif; + int buf_len = sizeof(struct hif_req_update_ie) + ies_len; + struct hif_req_update_ie *body = wfx_alloc_hif(buf_len, &hif); + + memcpy(&body->ie_flags, target_frame, sizeof(struct hif_ie_flags)); + body->num_i_es = cpu_to_le16(1); + memcpy(body->ie, ies, ies_len); + wfx_fill_header(hif, wvif->id, HIF_REQ_ID_UPDATE_IE, buf_len); + ret = wfx_cmd_send(wvif->wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_sl_send_pub_keys(struct wfx_dev *wdev, const uint8_t *pubkey, const uint8_t *pubkey_hmac) +{ + int ret; + struct hif_msg *hif; + struct hif_req_sl_exchange_pub_keys *body = wfx_alloc_hif(sizeof(*body), &hif); + + body->algorithm = HIF_SL_CURVE25519; + memcpy(body->host_pub_key, pubkey, sizeof(body->host_pub_key)); + memcpy(body->host_pub_key_mac, pubkey_hmac, sizeof(body->host_pub_key_mac)); + wfx_fill_header(hif, -1, HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + // Compatibility with legacy secure link + if (ret == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS) + ret = 0; + return ret; +} + +int hif_sl_config(struct wfx_dev *wdev, const unsigned long *bitmap) +{ + int ret; + struct hif_msg *hif; + struct hif_req_sl_configure *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body->encr_bmp, bitmap, sizeof(body->encr_bmp)); + wfx_fill_header(hif, -1, HIF_REQ_ID_SL_CONFIGURE, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + return ret; +} + +int hif_sl_set_mac_key(struct wfx_dev *wdev, const uint8_t *slk_key, int destination) +{ + int ret; + struct hif_msg *hif; + struct hif_req_set_sl_mac_key *body = wfx_alloc_hif(sizeof(*body), &hif); + + memcpy(body->key_value, slk_key, sizeof(body->key_value)); + body->otp_or_ram = destination; + wfx_fill_header(hif, -1, HIF_REQ_ID_SET_SL_MAC_KEY, sizeof(*body)); + ret = wfx_cmd_send(wdev, hif, NULL, 0, false); + kfree(hif); + // Compatibility with legacy secure link + if (ret == SL_MAC_KEY_STATUS_SUCCESS) + ret = 0; + return ret; +} diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h index ccf2b7e5e851..31f2a02c8466 100644 --- a/drivers/staging/wfx/hif_tx.h +++ b/drivers/staging/wfx/hif_tx.h @@ -15,6 +15,12 @@ struct wfx_dev; struct wfx_vif; +struct wfx_scan_params { + struct hif_req_start_scan scan_req; + struct hif_ssid_def *ssids; + uint8_t *ch; +}; + struct wfx_hif_cmd { struct mutex lock; struct completion ready; @@ -30,4 +36,31 @@ void wfx_init_hif_cmd(struct wfx_hif_cmd *wfx_hif_cmd); int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, size_t reply_len, bool async); +int hif_shutdown(struct wfx_dev *wdev); +int hif_configuration(struct wfx_dev *wdev, const u8 *conf, size_t len); +int hif_reset(struct wfx_vif *wvif, bool reset_stat); +int hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, + void *buf, size_t buf_size); +int hif_write_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, + void *buf, size_t buf_size); +int hif_scan(struct wfx_vif *wvif, const struct wfx_scan_params *arg); +int hif_stop_scan(struct wfx_vif *wvif); +int hif_join(struct wfx_vif *wvif, const struct hif_req_join *arg); +int hif_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg); +int hif_set_bss_params(struct wfx_vif *wvif, + const struct hif_req_set_bss_params *arg); +int hif_add_key(struct wfx_dev *wdev, const struct hif_req_add_key *arg); +int hif_remove_key(struct wfx_dev *wdev, int idx); +int hif_set_edca_queue_params(struct wfx_vif *wvif, + const struct hif_req_edca_queue_params *arg); +int hif_start(struct wfx_vif *wvif, const struct hif_req_start *arg); +int hif_beacon_transmit(struct wfx_vif *wvif, bool enable); +int hif_map_link(struct wfx_vif *wvif, u8 *mac_addr, int flags, int sta_id); +int hif_update_ie(struct wfx_vif *wvif, const struct hif_ie_flags *target_frame, + const u8 *ies, size_t ies_len); +int hif_sl_set_mac_key(struct wfx_dev *wdev, const uint8_t *slk_key, int destination); +int hif_sl_config(struct wfx_dev *wdev, const unsigned long *bitmap); +int hif_sl_send_pub_keys(struct wfx_dev *wdev, + const uint8_t *pubkey, const uint8_t *pubkey_hmac); + #endif diff --git a/drivers/staging/wfx/hif_tx_mib.h b/drivers/staging/wfx/hif_tx_mib.h new file mode 100644 index 000000000000..f6624a403016 --- /dev/null +++ b/drivers/staging/wfx/hif_tx_mib.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of host-to-chip MIBs of WFxxx Split Mac (WSM) API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + * Copyright (C) 2010, ST-Ericsson SA + */ +#ifndef WFX_HIF_TX_MIB_H +#define WFX_HIF_TX_MIB_H + +#include + +#include "wfx.h" +#include "hif_tx.h" +#include "hif_api_mib.h" + +static inline int hif_set_output_power(struct wfx_vif *wvif, int power_level) +{ + __le32 val = cpu_to_le32(power_level); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int hif_set_beacon_wakeup_period(struct wfx_vif *wvif, + unsigned int dtim_interval, + unsigned int listen_interval) +{ + struct hif_mib_beacon_wake_up_period val = { + .wakeup_period_min = dtim_interval, + .receive_dtim = 0, + .wakeup_period_max = cpu_to_le16(listen_interval), + }; + + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +static inline int hif_set_rcpi_rssi_threshold(struct wfx_vif *wvif, + struct hif_mib_rcpi_rssi_threshold *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_RCPI_RSSI_THRESHOLD, arg, sizeof(*arg)); +} + +static inline int hif_get_counters_table(struct wfx_dev *wdev, + struct hif_mib_extended_count_table *arg) +{ + if (wfx_api_older_than(wdev, 1, 3)) { + // extended_count_table is wider than count_table + memset(arg, 0xFF, sizeof(*arg)); + return hif_read_mib(wdev, 0, HIF_MIB_ID_COUNTERS_TABLE, + arg, sizeof(struct hif_mib_count_table)); + } else { + return hif_read_mib(wdev, 0, HIF_MIB_ID_EXTENDED_COUNTERS_TABLE, + arg, sizeof(struct hif_mib_extended_count_table)); + } +} + +static inline int hif_set_macaddr(struct wfx_vif *wvif, u8 *mac) +{ + struct hif_mib_mac_address msg = { }; + + if (mac) + ether_addr_copy(msg.mac_addr, mac); + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_DOT11_MAC_ADDRESS, + &msg, sizeof(msg)); +} + +static inline int hif_set_rx_filter(struct wfx_vif *wvif, bool filter_bssid, + bool fwd_probe_req) +{ + struct hif_mib_rx_filter val = { }; + + if (filter_bssid) + val.bssid_filter = 1; + if (fwd_probe_req) + val.fwd_probe_req = 1; + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_RX_FILTER, + &val, sizeof(val)); +} + +static inline int hif_set_beacon_filter_table(struct wfx_vif *wvif, + struct hif_mib_bcn_filter_table *ft) +{ + size_t buf_len = struct_size(ft, ie_table, ft->num_of_info_elmts); + + cpu_to_le32s(&ft->num_of_info_elmts); + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_FILTER_TABLE, ft, buf_len); +} + +static inline int hif_beacon_filter_control(struct wfx_vif *wvif, + int enable, int beacon_count) +{ + struct hif_mib_bcn_filter_enable arg = { + .enable = cpu_to_le32(enable), + .bcn_count = cpu_to_le32(beacon_count), + }; + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_BEACON_FILTER_ENABLE, &arg, sizeof(arg)); +} + +static inline int hif_set_operational_mode(struct wfx_dev *wdev, + enum hif_op_power_mode mode) +{ + struct hif_mib_gl_operational_power_mode val = { + .power_mode = mode, + .wup_ind_activation = 1, + }; + + return hif_write_mib(wdev, -1, HIF_MIB_ID_GL_OPERATIONAL_POWER_MODE, + &val, sizeof(val)); +} + +static inline int hif_set_template_frame(struct wfx_vif *wvif, + struct hif_mib_template_frame *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_TEMPLATE_FRAME, + arg, sizeof(*arg)); +} + +static inline int hif_set_mfp(struct wfx_vif *wvif, bool capable, bool required) +{ + struct hif_mib_protected_mgmt_policy val = { }; + + WARN_ON(required && !capable); + if (capable) { + val.pmf_enable = 1; + val.host_enc_auth_frames = 1; + } + if (!required) + val.unpmf_allowed = 1; + cpu_to_le32s(&val); + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); +} + +static inline int hif_set_block_ack_policy(struct wfx_vif *wvif, + u8 tx_tid_policy, u8 rx_tid_policy) +{ + struct hif_mib_block_ack_policy val = { + .block_ack_tx_tid_policy = tx_tid_policy, + .block_ack_rx_tid_policy = rx_tid_policy, + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_BLOCK_ACK_POLICY, + &val, sizeof(val)); +} + +static inline int hif_set_association_mode(struct wfx_vif *wvif, + struct hif_mib_set_association_mode *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_ASSOCIATION_MODE, arg, sizeof(*arg)); +} + +static inline int hif_set_tx_rate_retry_policy(struct wfx_vif *wvif, + struct hif_mib_set_tx_rate_retry_policy *arg) +{ + size_t size = struct_size(arg, tx_rate_retry_policy, arg->num_tx_rate_policies); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, size); +} + +static inline int hif_set_mac_addr_condition(struct wfx_vif *wvif, + struct hif_mib_mac_addr_data_frame_condition *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_MAC_ADDR_DATAFRAME_CONDITION, + arg, sizeof(*arg)); +} + +static inline int hif_set_uc_mc_bc_condition(struct wfx_vif *wvif, + struct hif_mib_uc_mc_bc_data_frame_condition *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_UC_MC_BC_DATAFRAME_CONDITION, + arg, sizeof(*arg)); +} + +static inline int hif_set_config_data_filter(struct wfx_vif *wvif, + struct hif_mib_config_data_filter *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_CONFIG_DATA_FILTER, arg, sizeof(*arg)); +} + +static inline int hif_set_data_filtering(struct wfx_vif *wvif, + struct hif_mib_set_data_filtering *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_DATA_FILTERING, arg, sizeof(*arg)); +} + +static inline int hif_keep_alive_period(struct wfx_vif *wvif, int period) +{ + struct hif_mib_keep_alive_period arg = { + .keep_alive_period = cpu_to_le16(period), + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +static inline int hif_set_arp_ipv4_filter(struct wfx_vif *wvif, + struct hif_mib_arp_ip_addr_table *fp) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +static inline int hif_use_multi_tx_conf(struct wfx_dev *wdev, + bool enabled) +{ + __le32 arg = enabled ? cpu_to_le32(1) : 0; + + return hif_write_mib(wdev, -1, HIF_MIB_ID_GL_SET_MULTI_MSG, + &arg, sizeof(arg)); +} + +static inline int hif_set_uapsd_info(struct wfx_vif *wvif, + struct hif_mib_set_uapsd_information *arg) +{ + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +static inline int hif_erp_use_protection(struct wfx_vif *wvif, bool enable) +{ + __le32 arg = enable ? cpu_to_le32(1) : 0; + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_NON_ERP_PROTECTION, &arg, sizeof(arg)); +} + +static inline int hif_slot_time(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val); + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_SLOT_TIME, + &arg, sizeof(arg)); +} + +static inline int hif_dual_cts_protection(struct wfx_vif *wvif, bool val) +{ + struct hif_mib_set_ht_protection arg = { + .dual_cts_prot = val, + }; + + return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_SET_HT_PROTECTION, + &arg, sizeof(arg)); +} + +static inline int hif_wep_default_key_id(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &arg, sizeof(arg)); +} + +static inline int hif_rts_threshold(struct wfx_vif *wvif, int val) +{ + __le32 arg = cpu_to_le32(val > 0 ? val : 0xFFFF); + + return hif_write_mib(wvif->wdev, wvif->id, + HIF_MIB_ID_DOT11_RTS_THRESHOLD, &arg, sizeof(arg)); +} + +#endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index bf9de11f8896..e23e86d4d7f0 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -10,6 +10,7 @@ #ifndef WFX_H #define WFX_H +#include #include #include @@ -18,6 +19,11 @@ #include "hif_tx.h" #include "hif_api_general.h" +#if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE) +#define struct_size(p, member, n) \ + (n * sizeof(*(p)->member) + __must_be_array((p)->member) + sizeof(*(p))) +#endif + struct hwbus_ops; struct wfx_dev { From patchwork Thu Sep 19 10:52:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152147 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C1E2C16B1 for ; Thu, 19 Sep 2019 10:54:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 828D621929 for ; Thu, 19 Sep 2019 10:54:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="EUlShXT7" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389558AbfISKxu (ORCPT ); Thu, 19 Sep 2019 06:53:50 -0400 Received: from mail-eopbgr700070.outbound.protection.outlook.com ([40.107.70.70]:25057 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389530AbfISKxB (ORCPT ); Thu, 19 Sep 2019 06:53:01 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=PtP9a8gyLtXGLkTXegNrZn+/ZWCZoAZpDft9FdCMqeBiuCYMSxyYqTqcZnkpnesBCSJ+KAxdcLQ2fPbrHFzUbpldvyXT3Ow23YLxMRV96S5IYP5mVnBFfza03iA2/9MHfReqQo0IBMD/kvzQKtGPwwIEZIak4KkWsDsymRC1ula2aFta5KIbq0Rhvd1X9LoX0gSokWbYK2Dvwi8Vrs/cSRxSmToIRE+3UxmqPoXMPVW02y7HxKHMMUN6nOj2eCpCNmgP0SyEKfTuOS0Aws+x1ZpvvjgA9ig+II0N+4IxIg0NaFbuKTao/WEv79TmM2e4cHebBKnrA+FuBCKuUHKhjw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5C9kfXMbn12ypnULFyareVwp29flsCThJ275sDBCOv4=; b=fF/i9tXSMfTCz1Fc5/0YHiIZui3LJ34K699EzVHEDwPzrtp/tIVIo5yCvcLZM0v1dni32zeJhSspVllxCKymKVhUhzyniSLe32DBqg+TZNona4J53bmQnkhMsefVsSy6/lobiB14NK1X8y8QpxFu7TfdM4CtEdOvJJ9JXAgsWuopfm5byNI6q9F0ouOLyGbvWNPt1QJZRIyKdI/R45kW3EgYgKr02ytJjlGcr1wtUwKnyJ3TUdxiAKJgNUHcwf1mmN9bZH87tuot8CDMAYwyPIvYLKZYw/DtJRmNUdEy2RHdxgjY2hUQxm9sD7J/k6fhhL4Bw6CNdoFjxekuCJ63UQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=5C9kfXMbn12ypnULFyareVwp29flsCThJ275sDBCOv4=; b=EUlShXT7U/vy2nyA637GXvSj+tHpUCIPYimnIatXfHSbd4aGipiSDK5UbWupIs98SuUN6zdoap0TCxAQVyZXuuWU0mBEnU0CiJuc5Ptw0nlfvgPsUYtC4EAjDaNXBz6GBZNvMWMoCPOBuGv9RvzpS+q4iExdj+ccQ95SlXbaq5M= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:42 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:42 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 13/20] staging: wfx: introduce "secure link" Thread-Topic: [PATCH 13/20] staging: wfx: introduce "secure link" Thread-Index: AQHVbthbolBcRWiHS06Fd0rImAs/Jg== Date: Thu, 19 Sep 2019 10:52:41 +0000 Message-ID: <20190919105153.15285-14-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: aaf14fa5-df5f-4b8c-c38a-08d73cef7f54 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:1284; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(52314003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(966005)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(6306002)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 5eCeSB+dVp21aXnlir2N/+olEdyITJq0y8v3zVhEWBYgaQgJXa/hbNuUYa3JPrl5ppGBLYaW2oolSrOETVDdkrNUTHwxfzdYJX2QaMupEwRaqhVuPOFpJPUPrXoWDuGfGE81JLtQOeBbG2VYdvqvVXu7oLtp+gUwER+t7UuL3vEuIrgTSFjZbARwSb39waXiayvy+y6oK9GBj9QhJ1TPQeq/a9F4vrHHrj4jG3ZkxNxxr6/uqrnB4SsSmpDsEh8BgCQHkG+he6F7fDaKnOvrcve9BvBubawDlV115U53r2UkcYLB+V4TswmEb/3eCduIo/JzPPsNCrNiZ1ife7wT9iqt9GWLv8OC3B+TEqURKPWwBxGnecICLt/dmIeIWQPw7UTVXLTm59O4lG/H205JtLWFqd8cyUYlXC3YAcWKTas= Content-ID: <082A904777DE1141A3098ED9EC9DF277@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: aaf14fa5-df5f-4b8c-c38a-08d73cef7f54 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:41.0875 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: i1nkCiVUuqev+8Tr6rJKcv1sytiJXMVbRgznPaylDpyVpiicYpe9+qe91evC0Pr0blXuWDfoO8ta7NapZP4eDg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Chip support encryption of the link between host and chip. This feature is called "secure link". Driver code on github[1] support it. However, it relies on mbedtls for cryptographic functions. So, I decided to not import this feature in current patch. However, in order to keep code synchronized between github and kernel, I imported all code related to this feature, even if most of it is just no-op. [1]: https://github.com/SiliconLabs/wfx-linux-driver/ Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/bh.c | 31 +++++++++++++++++++-- drivers/staging/wfx/debug.c | 17 ++++++++++++ drivers/staging/wfx/hif_rx.c | 17 ++++++++++++ drivers/staging/wfx/hif_tx.c | 6 ++++ drivers/staging/wfx/hif_tx.h | 1 + drivers/staging/wfx/main.c | 36 ++++++++++++++++++++++++ drivers/staging/wfx/main.h | 2 ++ drivers/staging/wfx/secure_link.h | 46 +++++++++++++++++++++++++++++++ drivers/staging/wfx/wfx.h | 2 ++ 9 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/secure_link.h diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index c94c9c401a69..d321fd312d55 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -12,6 +12,7 @@ #include "wfx.h" #include "hwio.h" #include "traces.h" +#include "secure_link.h" #include "hif_rx.h" #include "hif_api_cmd.h" @@ -74,7 +75,18 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) hif = (struct hif_msg *) skb->data; WARN(hif->encrypted & 0x1, "unsupported encryption type"); if (hif->encrypted == 0x2) { - BUG(); // Not yet implemented + if (wfx_sl_decode(wdev, (void *) hif)) { + dev_kfree_skb(skb); + // If frame was a confirmation, expect trouble in next + // exchange. However, it is harmless to fail to decode + // an indication frame, so try to continue. Anyway, + // piggyback is probably correct. + return piggyback; + } + le16_to_cpus(hif->len); + computed_len = round_up(hif->len - sizeof(hif->len), 16) + + sizeof(struct hif_sl_msg) + + sizeof(struct hif_sl_tag); } else { le16_to_cpus(hif->len); computed_len = round_up(hif->len, 2); @@ -166,7 +178,22 @@ static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif) hif->seqnum = wdev->hif.tx_seqnum; wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1); - data = hif; + if (wfx_is_secure_command(wdev, hif->id)) { + len = round_up(len - sizeof(hif->len), 16) + sizeof(hif->len) + + sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_sl_tag); + // AES support encryption in-place. However, mac80211 access to + // 802.11 header after frame was sent (to get MAC addresses). + // So, keep origin buffer clear. + data = kmalloc(len, GFP_KERNEL); + if (!data) + goto end; + is_encrypted = true; + ret = wfx_sl_encode(wdev, hif, data); + if (ret) + goto end; + } else { + data = hif; + } WARN(len > wdev->hw_caps.size_inp_ch_buf, "%s: request exceed WFx capability: %zu > %d\n", __func__, len, wdev->hw_caps.size_inp_ch_buf); diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index 0a328c96eaa0..f79693a4be7f 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -6,6 +6,7 @@ * Copyright (c) 2010, ST-Ericsson */ #include +#include #include "debug.h" #include "wfx.h" @@ -53,6 +54,21 @@ const char *get_reg_name(unsigned long id) return get_symbol(id, wfx_reg_print_map); } +static ssize_t wfx_burn_slk_key_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wfx_dev *wdev = file->private_data; + + dev_info(wdev->dev, "this driver does not support secure link\n"); + return -EINVAL; +} + +static const struct file_operations wfx_burn_slk_key_fops = { + .open = simple_open, + .write = wfx_burn_slk_key_write, +}; + struct dbgfs_hif_msg { struct wfx_dev *wdev; struct completion complete; @@ -146,6 +162,7 @@ int wfx_debug_init(struct wfx_dev *wdev) struct dentry *d; d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops); debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); return 0; diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index ba8ea4f3c91b..dd5f1dea4e85 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -11,6 +11,7 @@ #include "hif_rx.h" #include "wfx.h" +#include "secure_link.h" #include "hif_api_cmd.h" static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) @@ -46,6 +47,8 @@ static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void * } else { wdev->hif_cmd.buf_send = NULL; mutex_unlock(&wdev->hif_cmd.lock); + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); } return status; } @@ -68,11 +71,25 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi return 0; } +static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_sl_exchange_pub_keys *body = buf; + + // Compatibility with legacy secure link + if (body->status == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS) + body->status = 0; + if (body->status) + dev_warn(wdev->dev, "secure link negociation error\n"); + wfx_sl_check_pubkey(wdev, body->ncp_pub_key, body->ncp_pub_key_mac); + return 0; +} + static const struct { int msg_id; int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); } hif_handlers[] = { { HIF_IND_ID_STARTUP, hif_startup_indication }, + { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, }; void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c index 781a6e28dbad..f8ab871aa188 100644 --- a/drivers/staging/wfx/hif_tx.c +++ b/drivers/staging/wfx/hif_tx.c @@ -20,6 +20,7 @@ void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) init_completion(&hif_cmd->ready); init_completion(&hif_cmd->done); mutex_init(&hif_cmd->lock); + mutex_init(&hif_cmd->key_renew_lock); } static void wfx_fill_header(struct hif_msg *hif, int if_id, unsigned int cmd, size_t size) @@ -59,6 +60,9 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz if (wdev->chip_frozen) return -ETIMEDOUT; + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_lock(&wdev->hif_cmd.key_renew_lock); + mutex_lock(&wdev->hif_cmd.lock); WARN(wdev->hif_cmd.buf_send, "data locking error"); @@ -107,6 +111,8 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n", get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret); + if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS) + mutex_unlock(&wdev->hif_cmd.key_renew_lock); return ret; } diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h index 31f2a02c8466..6f2ea2f3a77d 100644 --- a/drivers/staging/wfx/hif_tx.h +++ b/drivers/staging/wfx/hif_tx.h @@ -23,6 +23,7 @@ struct wfx_scan_params { struct wfx_hif_cmd { struct mutex lock; + struct mutex key_renew_lock; struct completion ready; struct completion done; bool async; diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 8973eeb60eb8..0cfd6b2ec8d1 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -27,6 +27,7 @@ #include "bh.h" #include "sta.h" #include "debug.h" +#include "secure_link.h" #include "hif_api_cmd.h" #include "wfx_version.h" @@ -39,6 +40,10 @@ static int gpio_wakeup = -2; module_param(gpio_wakeup, int, 0644); MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none."); +static char *slk_key; +module_param(slk_key, charp, 0600); +MODULE_PARM_DESC(slk_key, "secret key for secure link (expect 64 hexdecimal digits)."); + static const struct ieee80211_ops wfx_ops = { .start = wfx_start, .stop = wfx_stop, @@ -84,6 +89,29 @@ struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *lab return ret; } +static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata) +{ + const char *ascii_key = NULL; + int ret = 0; + + if (slk_key) + ascii_key = slk_key; + if (!ascii_key) + ret = of_property_read_string(dev->of_node, "slk_key", &ascii_key); + if (ret == -EILSEQ || ret == -ENODATA) + dev_err(dev, "ignoring malformatted key from DT\n"); + if (!ascii_key) + return; + + ret = hex2bin(pdata->slk_key, ascii_key, sizeof(pdata->slk_key)); + if (ret) { + dev_err(dev, "ignoring malformatted key: %s\n", ascii_key); + memset(pdata->slk_key, 0, sizeof(pdata->slk_key)); + return; + } + dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n"); +} + struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, const struct hwbus_ops *hwbus_ops, @@ -113,6 +141,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->hwbus_ops = hwbus_ops; wdev->hwbus_priv = hwbus_priv; memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + wfx_fill_sl_key(dev, &wdev->pdata); init_completion(&wdev->firmware_ready); wfx_init_hif_cmd(&wdev->hif_cmd); @@ -167,6 +196,12 @@ int wfx_probe(struct wfx_dev *wdev) goto err1; } + err = wfx_sl_init(wdev); + if (err && wdev->hw_caps.capabilities.link_mode == SEC_LINK_ENFORCED) { + dev_err(wdev->dev, "chip require secure_link, but can't negociate it\n"); + goto err1; + } + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { eth_zero_addr(wdev->addresses[i].addr); macaddr = of_get_mac_address(wdev->dev->of_node); @@ -198,6 +233,7 @@ int wfx_probe(struct wfx_dev *wdev) void wfx_release(struct wfx_dev *wdev) { wfx_bh_unregister(wdev); + wfx_sl_deinit(wdev); } static int __init wfx_core_init(void) diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index f7c65999a493..2c9c215455ce 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -14,12 +14,14 @@ #include #include "bus.h" +#include "hif_api_general.h" struct wfx_dev; struct wfx_platform_data { /* Keyset and ".sec" extention will appended to this string */ const char *file_fw; + unsigned char slk_key[API_KEY_VALUE_SIZE]; struct gpio_desc *gpio_wakeup; /* * if true HIF D_out is sampled on the rising edge of the clock diff --git a/drivers/staging/wfx/secure_link.h b/drivers/staging/wfx/secure_link.h new file mode 100644 index 000000000000..e2da1c73c760 --- /dev/null +++ b/drivers/staging/wfx/secure_link.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, Silicon Laboratories, Inc. + */ +#ifndef WFX_SECURE_LINK_H +#define WFX_SECURE_LINK_H + +#include "hif_api_general.h" + +struct wfx_dev; + + +struct sl_context { +}; + +static inline bool wfx_is_secure_command(struct wfx_dev *wdev, int cmd_id) +{ + return false; +} + +static inline int wfx_sl_decode(struct wfx_dev *wdev, struct hif_sl_msg *m) +{ + return -EIO; +} + +static inline int wfx_sl_encode(struct wfx_dev *wdev, struct hif_msg *input, struct hif_sl_msg *output) +{ + return -EIO; +} + +static inline int wfx_sl_check_pubkey(struct wfx_dev *wdev, uint8_t *ncp_pubkey, uint8_t *ncp_pubmac) +{ + return -EIO; +} + +static inline int wfx_sl_init(struct wfx_dev *wdev) +{ + return -EIO; +} + +static inline void wfx_sl_deinit(struct wfx_dev *wdev) +{ +} + + +#endif diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index e23e86d4d7f0..2537fc97af27 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -16,6 +16,7 @@ #include "bh.h" #include "main.h" +#include "secure_link.h" #include "hif_tx.h" #include "hif_api_general.h" @@ -39,6 +40,7 @@ struct wfx_dev { struct completion firmware_ready; struct hif_ind_startup hw_caps; struct wfx_hif hif; + struct sl_context sl; int chip_frozen; struct wfx_hif_cmd hif_cmd; From patchwork Thu Sep 19 10:52:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152169 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EB53014DB for ; Thu, 19 Sep 2019 10:55:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AB9CA21924 for ; Thu, 19 Sep 2019 10:55:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="KZZZdT/h" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389772AbfISKzH (ORCPT ); Thu, 19 Sep 2019 06:55:07 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389415AbfISKwr (ORCPT ); Thu, 19 Sep 2019 06:52:47 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=NOEKMqxnaZC4Ec/Ic76Yuvu2Pf7gaNJfO57/E0sfwW1Dz2XohXKUEWC3CyI/+FFj+Hc5stp9BxuLzLjVUYccfZeSU04oeSRGRDhhhr+UPi9KytKaiLGXWKM7e+TJnTzpB01NpoOuvlx4OfypEpb+961s8FbebDV2r6qHzFgMtBzRQSTO+2PB11ljh86eVt9R4gmbsFZeW7Dyc9Aa/Em0/SCzdvC64Ar5dz0ZSJamuArKBZaf3LRoTisR6gH/6YfW1XnKqA3KODKg0Q4HnyU+SlzVEBQ93tEur7OL8QGWNh1wDBrEL6PB7w9JMXVdEE59Y8w7o4h2gwrkR8rSd3mzsA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GAuEJd1m/W2ssuVkuPzuJbm16vtuvyO128flRB+a6iI=; b=jpcCjLgRrKZkqN4KRhTBNld3T+9LTxEic73z1tzlU5vTBGaYSygUNBYs8HqAN4uogeFvvHGln/F1CdWlTlEhZxCyOywS84Md1o09DDFNUC/5iJAWnaKBdev3uaajhIzdtbWKFyS4YmhDLiP9Z+kKIZWcOBJQmb2PrCCN+nrwnHDvocH8ZWF9viGPC3sq/vlFFOcVBqihVzkd9BTrz8ahEaQM3QQFGQHlr/8QbAyJH+i5X5qq4JPm8OgrbsDBGB97FGlaO93NBy5zIMy+erjm+0GTt3EX+pcNsIloYWxGrV6AxmDCPVWO/wHfdCHN9x3bj9GK+fSI3pT7kJFmYZslHw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GAuEJd1m/W2ssuVkuPzuJbm16vtuvyO128flRB+a6iI=; b=KZZZdT/hVyzd9wjg5LMLMfTx0LDgYGi4WwoEbnt9OP983hC/cMIYerHogziAd/zP7cPhJFetYTB/szZs0SSPddC8Y36Rhd/d77ac2jO1u4RIV0YE4Oj6rpv/21saCSZTjh0IABI5PodpauHvCEbPr4QHx9ZVDE2fwNGQUN4/SFc= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:43 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:43 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 14/20] staging: wfx: setup initial chip configuration Thread-Topic: [PATCH 14/20] staging: wfx: setup initial chip configuration Thread-Index: AQHVbthcHyoij0GWT0m4YSubLjb5JA== Date: Thu, 19 Sep 2019 10:52:41 +0000 Message-ID: <20190919105153.15285-15-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 9983e75a-1d3e-4edb-be9a-08d73cef7f9c x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-purlcount: 1 x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:494; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(966005)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(6306002)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 6gJhdR4ZSvt+/K+ex8/HJVRu5CuOTbKWNsUMtn40g/LUWoICa1DfSlnB2c09kw2YpxoA1c9L+KRP3TQw4RmMuiM/5PVGBmvrNI3qRA3Jorrqov15EznD5+ImnWf9jSwwfnkYEWY67NO2Zm4a6nB/Z5QjDXz1d6sgLEGJRV/Cx0C9G3fpnrBqjAuQ3Jk8pLMQnLVwaZF0AjjGqwE8+A+hl/aoH+JX8YcSbT/DWns9Le2492WiG9OJPdPW06WjHKg79NJphbyMnSKT643hS1UxzUdDree8VggJUEWbgpuAnnUJE3pHXL+TFbfhnZ7P3F94zopBX3mKo+zfnMIK0QxlU2QHEs6fSweiq+PcSuJ8sxxelMP6kSLvJ6RP0yeOGeHAFnScXLRicrDgxiZ52Kk7UEPvJSKWLpT3CKGcbRqCviA= Content-ID: <2614ED3FB5F31642B6561BB79EA5145D@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9983e75a-1d3e-4edb-be9a-08d73cef7f9c X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:41.5672 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: vUAwRDtkDgdscXFIwhrHuiLb9PEkS8j26yt6Z6GNlXIk0mZ9aAzYlI0zW5m9DILDOCFcYzWppjq95FjF+/eSqA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller A few tasks remain to be done in order to finish chip initial configuration: - configure chip to use multi-tx confirmation (speed up data transfer) - configure chip to use wake-up feature (save power consumption during runtime) - set hardware configuration (clocks, RF, pinout, etc...) using a Platform Data Set (PDS) file On release, driver completely shutdown the chip to save power consumption. Documentation about PDS and PDS data for sample boards are available here[1]. One day, PDS data may find a place in device tree but, currently, PDS is too much linked with firmware to allowing that. This patch also add "send_pds" file in debugfs to be able to dynamically change PDS (only for debug, of course). [1]: https://github.com/SiliconLabs/wfx-firmware/tree/master/PDS Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/bus_sdio.c | 1 + drivers/staging/wfx/bus_spi.c | 1 + drivers/staging/wfx/debug.c | 29 +++++++++++ drivers/staging/wfx/hif_rx.c | 11 ++++ drivers/staging/wfx/main.c | 94 ++++++++++++++++++++++++++++++++++ drivers/staging/wfx/main.h | 2 + 6 files changed, 138 insertions(+) diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index c0c063c3cfc9..05f02c278782 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -19,6 +19,7 @@ static const struct wfx_platform_data wfx_sdio_pdata = { .file_fw = "wfm_wf200", + .file_pds = "wf200.pds", }; struct wfx_sdio_priv { diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index 8a9aab3e7384..163342b66a5e 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -32,6 +32,7 @@ MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); static const struct wfx_platform_data wfx_spi_pdata = { .file_fw = "wfm_wf200", + .file_pds = "wf200.pds", .use_rising_clk = true, }; diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index f79693a4be7f..0619c7d1cf79 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -10,6 +10,7 @@ #include "debug.h" #include "wfx.h" +#include "main.h" #define CREATE_TRACE_POINTS #include "traces.h" @@ -54,6 +55,33 @@ const char *get_reg_name(unsigned long id) return get_symbol(id, wfx_reg_print_map); } +static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wfx_dev *wdev = file->private_data; + char *buf; + int ret; + + if (*ppos != 0) { + dev_dbg(wdev->dev, "PDS data must be written in one transaction"); + return -EBUSY; + } + buf = memdup_user(user_buf, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + *ppos = *ppos + count; + ret = wfx_send_pds(wdev, buf, count); + kfree(buf); + if (ret < 0) + return ret; + return count; +} + +static const struct file_operations wfx_send_pds_fops = { + .open = simple_open, + .write = wfx_send_pds_write, +}; + static ssize_t wfx_burn_slk_key_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -162,6 +190,7 @@ int wfx_debug_init(struct wfx_dev *wdev) struct dentry *d; d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops); debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops); debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index dd5f1dea4e85..6b9683d69a3f 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -71,6 +71,16 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi return 0; } +static int hif_wakeup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + if (!wdev->pdata.gpio_wakeup + || !gpiod_get_value(wdev->pdata.gpio_wakeup)) { + dev_warn(wdev->dev, "unexpected wake-up indication\n"); + return -EIO; + } + return 0; +} + static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct hif_ind_sl_exchange_pub_keys *body = buf; @@ -89,6 +99,7 @@ static const struct { int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); } hif_handlers[] = { { HIF_IND_ID_STARTUP, hif_startup_indication }, + { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, }; diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 0cfd6b2ec8d1..5b04ea5f4353 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "main.h" #include "wfx.h" @@ -28,9 +29,12 @@ #include "sta.h" #include "debug.h" #include "secure_link.h" +#include "hif_tx_mib.h" #include "hif_api_cmd.h" #include "wfx_version.h" +#define WFX_PDS_MAX_SIZE 1500 + MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx"); MODULE_AUTHOR("Jérôme Pouiller "); MODULE_LICENSE("GPL"); @@ -112,6 +116,69 @@ static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata) dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n"); } +/* NOTE: wfx_send_pds() destroy buf */ +int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len) +{ + int ret; + int start, brace_level, i; + + start = 0; + brace_level = 0; + if (buf[0] != '{') { + dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to compress it?\n"); + return -EINVAL; + } + for (i = 1; i < len - 1; i++) { + if (buf[i] == '{') + brace_level++; + if (buf[i] == '}') + brace_level--; + if (buf[i] == '}' && !brace_level) { + i++; + if (i - start + 1 > WFX_PDS_MAX_SIZE) + return -EFBIG; + buf[start] = '{'; + buf[i] = 0; + dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start); + buf[i] = '}'; + ret = hif_configuration(wdev, buf + start, i - start + 1); + if (ret == HIF_STATUS_FAILURE) { + dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported options?)\n", start, i); + return -EINVAL; + } + if (ret == -ETIMEDOUT) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted file?)\n", start, i); + return ret; + } + if (ret) { + dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown error\n", start, i); + return -EIO; + } + buf[i] = ','; + start = i; + } + } + return 0; +} + +static int wfx_send_pdata_pds(struct wfx_dev *wdev) +{ + int ret = 0; + const struct firmware *pds; + unsigned char *tmp_buf; + + ret = request_firmware(&pds, wdev->pdata.file_pds, wdev->dev); + if (ret) { + dev_err(wdev->dev, "can't load PDS file %s\n", wdev->pdata.file_pds); + return ret; + } + tmp_buf = kmemdup(pds->data, pds->size, GFP_KERNEL); + ret = wfx_send_pds(wdev, tmp_buf, pds->size); + kfree(tmp_buf); + release_firmware(pds); + return ret; +} + struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, const struct hwbus_ops *hwbus_ops, @@ -141,6 +208,8 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->hwbus_ops = hwbus_ops; wdev->hwbus_priv = hwbus_priv; memcpy(&wdev->pdata, pdata, sizeof(*pdata)); + of_property_read_string(dev->of_node, "config-file", &wdev->pdata.file_pds); + wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup"); wfx_fill_sl_key(dev, &wdev->pdata); init_completion(&wdev->firmware_ready); @@ -159,6 +228,12 @@ int wfx_probe(struct wfx_dev *wdev) int i; int err; const void *macaddr; + struct gpio_desc *gpio_saved; + + // During first part of boot, gpio_wakeup cannot yet been used. So + // prevent bh() to touch it. + gpio_saved = wdev->pdata.gpio_wakeup; + wdev->pdata.gpio_wakeup = NULL; wfx_bh_register(wdev); @@ -202,6 +277,24 @@ int wfx_probe(struct wfx_dev *wdev) goto err1; } + dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds); + err = wfx_send_pdata_pds(wdev); + if (err < 0) + goto err1; + + wdev->pdata.gpio_wakeup = gpio_saved; + if (wdev->pdata.gpio_wakeup) { + dev_dbg(wdev->dev, "enable 'quiescent' power mode with gpio %d and PDS file %s\n", + desc_to_gpio(wdev->pdata.gpio_wakeup), wdev->pdata.file_pds); + gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + control_reg_write(wdev, 0); + hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT); + } else { + hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_DOZE); + } + + hif_use_multi_tx_conf(wdev, true); + for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) { eth_zero_addr(wdev->addresses[i].addr); macaddr = of_get_mac_address(wdev->dev->of_node); @@ -232,6 +325,7 @@ int wfx_probe(struct wfx_dev *wdev) void wfx_release(struct wfx_dev *wdev) { + hif_shutdown(wdev); wfx_bh_unregister(wdev); wfx_sl_deinit(wdev); } diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index 2c9c215455ce..f2b07ed1627c 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -21,6 +21,7 @@ struct wfx_dev; struct wfx_platform_data { /* Keyset and ".sec" extention will appended to this string */ const char *file_fw; + const char *file_pds; unsigned char slk_key[API_KEY_VALUE_SIZE]; struct gpio_desc *gpio_wakeup; /* @@ -42,5 +43,6 @@ void wfx_release(struct wfx_dev *wdev); struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label); bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor); +int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len); #endif From patchwork Thu Sep 19 10:52:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152141 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4868A1747 for ; Thu, 19 Sep 2019 10:52:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2870A217D6 for ; Thu, 19 Sep 2019 10:52:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="LiUNSFn5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389483AbfISKwv (ORCPT ); Thu, 19 Sep 2019 06:52:51 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389435AbfISKwt (ORCPT ); Thu, 19 Sep 2019 06:52:49 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Spwa4+kfXCn6TEfv8QZwnjS2JUC5r6nCQeyZ06aZV/f3pcnfa0t0O+EcUrtqaLQCxcGrxZmNj0MOqTOsCyoV+DCR1eq2VHK6tmgzbmVacrJbJsU+r8ntdfQJZDJ4AyT7tC8gbz8l/S7Mwrsm+Fg/w3TyOUSwQkLzPXo3rrbCVm5ri0noKUay8F/BD4X1Tin68dgu7Du9MV5LzNArd3yBRgbxrNasDzK/gcpCwlNJocpZcZoe/ryjEpeVqZtwpbOaNd0GDDdxqYCIjB3FwyEsmaOj+pBNJkdXl6bQzCxQC0CNkXLptS/yyhb+PBoMwmVwEpkF0zZKsygjcVFVyCrKvA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=IqrvIRLiKZ/bvt54De92KMZWa6wNK8oZCzID2iGcttU=; b=iepGFPIJf1RZFkAquvz5j2nqR2ybTpqA/fGdEQrlRPYpM4JbJySIhF8RkFkXuBGuDFHr5I5Z0Jm57zqRgWeHiDYESUlwLUHkvVTaxxsBGLmDESoqE7BoTcstGYO8G4zEogR0HNrR0iXNbbsUTpUwLhHxn54RVYXwjn4VytZ+Vwv+zwbGiz/sYFqARyXpUZRGmnpZODab7MCXVFGhM6oWQRCp6jingAkm7PxVVxs2Qk7E/EHfVkvHe15UhF+wE8WcXlIWFV7x9m7FYLjhLvmahjmtZs3FCuflq4sSNgQCMty0EgE43YQYVB+hf7oiDpiFMsk6CoWz2NRJ+yIgzLqzAQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=IqrvIRLiKZ/bvt54De92KMZWa6wNK8oZCzID2iGcttU=; b=LiUNSFn5keDeFZ0L33Bcb7mm25Moi7yC/l0k6sgMVUs0LGCkiah3KFxakAeEsf0HZlkO0sEMIe0hpu2DjjUnX3xsEPzLVPhRbCx9ud4GH7GkbEFTdhUaPh1bPRTmtAdJZ+R2T/3xxsDTgLrg9eg4LDT+tOuCDRCfu3VokBcWFck= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:43 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:43 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 15/20] staging: wfx: add debug files and trace debug events Thread-Topic: [PATCH 15/20] staging: wfx: add debug files and trace debug events Thread-Index: AQHVbthcrJiWXQfj6UGo6oVHge3Vfg== Date: Thu, 19 Sep 2019 10:52:42 +0000 Message-ID: <20190919105153.15285-16-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8a1f5d0c-6b13-4bbf-7483-08d73cef7fe0 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:327; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 2n/FNcI9TRSE7tk8Wto8aIFgjZrZLKJdt8LZNPzrl6XnVcf+EJwahkPGwsWyEg1T++Z5bFF5wphKd6wUsrD+9ZSiro5tsbrkMOPi/qybQzZghZ6SQ0dR7y+2SDGuXMfOrW0N9UwHp0qh/5tB8Em33KnVgTDDrqKcvdkW0zdKuHey6NXT+iggEZUNQhAxhryAYc3xBVTMFZwIQnCIr/kUDxdfJmqMcmLnXsKScC9StTVdx7fj0X4fPautHwnrcbJgHrHPpfFWzXZ5XQ1D2MOoVqgdO/bdCv1IuCdR8JCjOFt52b3xZ1i8LBS0XDYWlGLK/14IR33IodttVgsu/vKVkwvVonk2uUaGQR1ZDU69MRd12sWRigNaWGeX9f2k/27OMEl5knGeKBmbFZVT/hGU83mY7dz8uBc3dhKVYkHCewg= Content-ID: <4467A1877F7F7144A9AED367A8ED793E@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8a1f5d0c-6b13-4bbf-7483-08d73cef7fe0 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:42.0230 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: vz9MYsQullvIqSe0jO8hQxQsc5tdHZK13OpD5vlHdGDmGdpxfNynYXT7xtIa8ypxQqQIjOKhfUc2yWIpMPUT7A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Add traces when debug events happen and allow to ask internal information to chip. These features work independently from mac80211. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/debug.c | 122 +++++++++++++++++++++++++++++++++++ drivers/staging/wfx/hif_rx.c | 80 +++++++++++++++++++++++ drivers/staging/wfx/main.c | 2 + drivers/staging/wfx/wfx.h | 16 +++++ 4 files changed, 220 insertions(+) diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index 0619c7d1cf79..4bd9a079cbd9 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -5,16 +5,35 @@ * Copyright (c) 2017-2019, Silicon Laboratories, Inc. * Copyright (c) 2010, ST-Ericsson */ +#include #include +#include #include #include "debug.h" #include "wfx.h" #include "main.h" +#include "hif_tx_mib.h" #define CREATE_TRACE_POINTS #include "traces.h" +#if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE) +#define DEFINE_SHOW_ATTRIBUTE(__name) \ +static int __name ## _open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, __name ## _show, inode->i_private); \ +} \ + \ +static const struct file_operations __name ## _fops = { \ + .owner = THIS_MODULE, \ + .open = __name ## _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} +#endif + static const struct trace_print_flags hif_msg_print_map[] = { hif_msg_list, }; @@ -55,6 +74,107 @@ const char *get_reg_name(unsigned long id) return get_symbol(id, wfx_reg_print_map); } +static int wfx_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct wfx_dev *wdev = seq->private; + struct hif_mib_extended_count_table counters; + + ret = hif_get_counters_table(wdev, &counters); + if (ret < 0) + return ret; + if (ret > 0) + return -EIO; + +#define PUT_COUNTER(name) \ + seq_printf(seq, "%24s %d\n", #name ":", le32_to_cpu(counters.count_##name)) + + PUT_COUNTER(tx_packets); + PUT_COUNTER(tx_multicast_frames); + PUT_COUNTER(tx_frames_success); + PUT_COUNTER(tx_frame_failures); + PUT_COUNTER(tx_frames_retried); + PUT_COUNTER(tx_frames_multi_retried); + + PUT_COUNTER(rts_success); + PUT_COUNTER(rts_failures); + PUT_COUNTER(ack_failures); + + PUT_COUNTER(rx_packets); + PUT_COUNTER(rx_frames_success); + PUT_COUNTER(rx_packet_errors); + PUT_COUNTER(plcp_errors); + PUT_COUNTER(fcs_errors); + PUT_COUNTER(rx_decryption_failures); + PUT_COUNTER(rx_mic_failures); + PUT_COUNTER(rx_no_key_failures); + PUT_COUNTER(rx_frame_duplicates); + PUT_COUNTER(rx_multicast_frames); + PUT_COUNTER(rx_cmacicv_errors); + PUT_COUNTER(rx_cmac_replays); + PUT_COUNTER(rx_mgmt_ccmp_replays); + + PUT_COUNTER(rx_beacon); + PUT_COUNTER(miss_beacon); + +#undef PUT_COUNTER + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(wfx_counters); + +static const char * const channel_names[] = { + [0] = "1M", + [1] = "2M", + [2] = "5.5M", + [3] = "11M", + /* Entries 4 and 5 does not exist */ + [6] = "6M", + [7] = "9M", + [8] = "12M", + [9] = "18M", + [10] = "24M", + [11] = "36M", + [12] = "48M", + [13] = "54M", + [14] = "MCS0", + [15] = "MCS1", + [16] = "MCS2", + [17] = "MCS3", + [18] = "MCS4", + [19] = "MCS5", + [20] = "MCS6", + [21] = "MCS7", +}; + +static int wfx_rx_stats_show(struct seq_file *seq, void *v) +{ + struct wfx_dev *wdev = seq->private; + struct hif_rx_stats *st = &wdev->rx_stats; + int i; + + mutex_lock(&wdev->rx_stats_lock); + seq_printf(seq, "Timestamp: %dus\n", st->date); + seq_printf(seq, "Low power clock: frequency %uHz, external %s\n", + st->pwr_clk_freq, + st->is_ext_pwr_clk ? "yes" : "no"); + seq_printf(seq, "Num. of frames: %d, PER (x10e4): %d, Throughput: %dKbps/s\n", + st->nb_rx_frame, st->per_total, st->throughput); + seq_puts(seq, " Num. of PER RSSI SNR CFO\n"); + seq_puts(seq, " frames (x10e4) (dBm) (dB) (kHz)\n"); + for (i = 0; i < ARRAY_SIZE(channel_names); i++) { + if (channel_names[i]) + seq_printf(seq, "%5s %8d %8d %8d %8d %8d\n", + channel_names[i], st->nb_rx_by_rate[i], + st->per[i], st->rssi[i] / 100, + st->snr[i] / 100, st->cfo[i]); + } + mutex_unlock(&wdev->rx_stats_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(wfx_rx_stats); + static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -190,6 +310,8 @@ int wfx_debug_init(struct wfx_dev *wdev) struct dentry *d; d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); + debugfs_create_file("counters", 0444, d, wdev, &wfx_counters_fops); + debugfs_create_file("rx_stats", 0444, d, wdev, &wfx_rx_stats_fops); debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops); debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops); debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops); diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index 6b9683d69a3f..c93bae1b6acf 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -94,13 +94,93 @@ static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void * return 0; } +static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + dev_warn(wdev->dev, "unattended JoinCompleteInd\n"); + + return 0; +} + +static int hif_error_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_error *body = buf; + u8 *pRollback = (u8 *) body->data; + u32 *pStatus = (u32 *) body->data; + + switch (body->type) { + case HIF_ERROR_FIRMWARE_ROLLBACK: + dev_err(wdev->dev, "asynchronous error: firmware rollback error %d\n", *pRollback); + break; + case HIF_ERROR_FIRMWARE_DEBUG_ENABLED: + dev_err(wdev->dev, "asynchronous error: firmware debug feature enabled\n"); + break; + case HIF_ERROR_OUTDATED_SESSION_KEY: + dev_err(wdev->dev, "asynchronous error: secure link outdated key: %#.8x\n", *pStatus); + break; + case HIF_ERROR_INVALID_SESSION_KEY: + dev_err(wdev->dev, "asynchronous error: invalid session key\n"); + break; + case HIF_ERROR_OOR_VOLTAGE: + dev_err(wdev->dev, "asynchronous error: out-of-range overvoltage: %#.8x\n", *pStatus); + break; + case HIF_ERROR_PDS_VERSION: + dev_err(wdev->dev, "asynchronous error: wrong PDS payload or version: %#.8x\n", *pStatus); + break; + default: + dev_err(wdev->dev, "asynchronous error: unknown (%d)\n", body->type); + break; + } + return 0; +} + +static int hif_generic_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_ind_generic *body = buf; + + switch (body->indication_type) { + case HIF_GENERIC_INDICATION_TYPE_RAW: + return 0; + case HIF_GENERIC_INDICATION_TYPE_STRING: + dev_info(wdev->dev, "firmware says: %s\n", (char *) body->indication_data.raw_data); + return 0; + case HIF_GENERIC_INDICATION_TYPE_RX_STATS: + mutex_lock(&wdev->rx_stats_lock); + // Older firmware send a generic indication beside RxStats + if (!wfx_api_older_than(wdev, 1, 4)) + dev_info(wdev->dev, "Rx test ongoing. Temperature: %d°C\n", body->indication_data.rx_stats.current_temp); + memcpy(&wdev->rx_stats, &body->indication_data.rx_stats, sizeof(wdev->rx_stats)); + mutex_unlock(&wdev->rx_stats_lock); + return 0; + default: + dev_err(wdev->dev, "generic_indication: unknown indication type: %#.8x\n", body->indication_type); + return -EIO; + } +} + +static int hif_exception_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + size_t len = hif->len - 4; // drop header + dev_err(wdev->dev, "firmware exception\n"); + print_hex_dump_bytes("Dump: ", DUMP_PREFIX_NONE, buf, len); + wdev->chip_frozen = 1; + + return -1; +} + static const struct { int msg_id; int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); } hif_handlers[] = { { HIF_IND_ID_STARTUP, hif_startup_indication }, { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, + { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication }, { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, + { HIF_IND_ID_GENERIC, hif_generic_indication }, + { HIF_IND_ID_ERROR, hif_error_indication }, + { HIF_IND_ID_EXCEPTION, hif_exception_indication }, }; void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 5b04ea5f4353..2e71f446d4d4 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -212,6 +212,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup"); wfx_fill_sl_key(dev, &wdev->pdata); + mutex_init(&wdev->rx_stats_lock); init_completion(&wdev->firmware_ready); wfx_init_hif_cmd(&wdev->hif_cmd); @@ -220,6 +221,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, void wfx_free_common(struct wfx_dev *wdev) { + mutex_destroy(&wdev->rx_stats_lock); ieee80211_free_hw(wdev->hw); } diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 2537fc97af27..48071e1c989c 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -44,6 +44,9 @@ struct wfx_dev { int chip_frozen; struct wfx_hif_cmd hif_cmd; + + struct hif_rx_stats rx_stats; + struct mutex rx_stats_lock; }; struct wfx_vif { @@ -52,4 +55,17 @@ struct wfx_vif { int id; }; +static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) +{ + if (vif_id >= ARRAY_SIZE(wdev->vif)) { + dev_dbg(wdev->dev, "requesting non-existent vif: %d\n", vif_id); + return NULL; + } + if (!wdev->vif[vif_id]) { + dev_dbg(wdev->dev, "requesting non-allocated vif: %d\n", vif_id); + return NULL; + } + return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv; +} + #endif /* WFX_H */ From patchwork Thu Sep 19 10:52:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152149 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 429C11747 for ; Thu, 19 Sep 2019 10:54:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EBA7B2196E for ; Thu, 19 Sep 2019 10:54:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="SNK4hoQB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389692AbfISKxt (ORCPT ); Thu, 19 Sep 2019 06:53:49 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389485AbfISKxB (ORCPT ); Thu, 19 Sep 2019 06:53:01 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eObrBD8g3TqhCx3uiBWA/4ZUHasImlGmQclxKG8c1EDbJw4i0x1r5C+5vhj6EvHhn2EqNgBxJSQoT3nGuTi2beszJ8phK4gQDufD1OnoJ7oP0Qr6kfDd7qytCjrbIKo/etP5c05NWCWEdgCbFN0SP8fTICtbpXYAmzoVddbHfNHyxZUJ7LaTBM8nlY/dvJ9U4mrIgA4U3lJyZukJ6UjUbNUuIQX+VEoCjPa4MMDrACg7wMTK7CAJWDs9W2a7FGwS6XQStCdvt0ZtpieArCWffq8dBnpcEb0S+SWe/LPnNERBXKwjZM37gQfW6NNuAIKtt697jaiwXdkxrs9SJ9cc5Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=T9FwiM1PewI4izDyUFtXEkwLjKyrhFJgiDgP0ZfVuAI=; b=Pak5/cnraDRUPBafSZSdXcQaBO4Y7IeCjLmBFdk/snxOZQzQeVQombHX5wB6vdxrwAh/7qHR6dWq5kA/c3/b+HCr+hvRboqQuheEq7HJNKSFXSukj0ZvLqf8r/wRZdKRVzJs2c0K/+FgE4bdaOth5VK7olQxSO0bZ7yRyOVRdfsEyX0IP7eOL0A6Vpyxqo05p7BmRX88BOpqOp6np+NmAkzaE7GOy08R7OLWOUT4o61jhlbSGM8O+YCygWWR74Q7DgTllwuQP4VuXHbBHz//V3R7+gMnAO2xLp5Vzl3EGgYbUL3MFb4tVuacKyV+GiDMkxx/gqC8/SyMoFxw5EOsgA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=T9FwiM1PewI4izDyUFtXEkwLjKyrhFJgiDgP0ZfVuAI=; b=SNK4hoQBo8GCFzCp2lY+7fd9mPA6sdrTI3SLWKpC9MAvfOQ9XQUgpuKOzMscRskQ0dZTDTbrikCSQEb5A/YIZKkwzKZQw3yzwS0KY7JV7gDaz0MjDpUCa210tDKSy9nsNXtHmjAPVV6fnmcwTAE7+DcPWW2BnFbcgPSkP+N4pUo= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:44 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:44 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 16/20] staging: wfx: allow to send 802.11 frames Thread-Topic: [PATCH 16/20] staging: wfx: allow to send 802.11 frames Thread-Index: AQHVbthce/oxyB/cWEOY9AkWlrXJYA== Date: Thu, 19 Sep 2019 10:52:42 +0000 Message-ID: <20190919105153.15285-17-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 4aa693fe-25ab-4b91-12d2-08d73cef8040 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:5236; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(30864003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004)(569006);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: +jC+i2D38zpOWb+kkNiJAX3V0/ECijOrYRRp/XVAQMxAc4L57pOC1QLNdvWFACIOT7NdODjXwwc1hLSLp6a1y29sTF8Fv4lDLrPyGJ+ukM7IaZTOP+SU5dJRe1Gu7GbDglnt7wxcuHV/OlLmXJVPYoGcqZvTwTOCNv39RcZI3NNrj2Q0jg30/73q2dRXE6qdw82yOTb2pS92zLjg5Qte9kCwQD3WVp7GRKGFNFirJYg4oV6zrpRVSl2htlmyYy7LoCKSy/OTTJooMLjpBjS5CmMQa6z3oDTq/f9qCo7i+hXbrsnFm7UyjqN0RJV6m9J1ZYoYG4nZDimI1rWEN4VLb0LiBE682OMURAJsZqUac9i31jRqPCraGqld33tcFzjQmaa6JO5HEzsxlbK5mj9lOvU83euwni+9BYhYrMlW6Ac= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4aa693fe-25ab-4b91-12d2-08d73cef8040 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:42.8305 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: zr/votypORVz9OmFLHxqiuK+qSm44BxuFOmWrRzEc2AXK3mUSZnbtxRLjNwCGc0k22pV4ULbnUESCByiGrWzCA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Three things make this task more complex than it should: - Chip necessitate to associate a link-id to each station. It is same thing than association ID but, using 8 bits only. - Rate policy is sent separately from Tx frames - Driver try to handle itself power saving of stations and multicast data Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 3 + drivers/staging/wfx/bh.c | 2 + drivers/staging/wfx/data_tx.c | 783 ++++++++++++++++++++++++++++++++++ drivers/staging/wfx/data_tx.h | 93 ++++ drivers/staging/wfx/hif_rx.c | 37 ++ drivers/staging/wfx/hif_tx.c | 1 + drivers/staging/wfx/main.c | 4 + drivers/staging/wfx/queue.c | 526 +++++++++++++++++++++++ drivers/staging/wfx/queue.h | 59 +++ drivers/staging/wfx/sta.c | 145 +++++++ drivers/staging/wfx/sta.h | 8 + drivers/staging/wfx/traces.h | 74 ++++ drivers/staging/wfx/wfx.h | 73 ++++ 13 files changed, 1808 insertions(+) create mode 100644 drivers/staging/wfx/data_tx.c create mode 100644 drivers/staging/wfx/data_tx.h create mode 100644 drivers/staging/wfx/queue.c create mode 100644 drivers/staging/wfx/queue.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index e158589468a3..d5ac9fafd1f1 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -9,6 +9,9 @@ wfx-y := \ fwio.o \ hif_tx.o \ hif_rx.o \ + queue.o \ + data_tx.o \ + sta.o \ main.o \ sta.o \ debug.o diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index d321fd312d55..ed81c3924d98 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -220,6 +220,8 @@ static int bh_work_tx(struct wfx_dev *wdev, int max_msg) if (try_wait_for_completion(&wdev->hif_cmd.ready)) { WARN(!mutex_is_locked(&wdev->hif_cmd.lock), "data locking error"); hif = wdev->hif_cmd.buf_send; + } else { + hif = wfx_tx_queues_get(wdev); } } if (!hif) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c new file mode 100644 index 000000000000..217d3c270706 --- /dev/null +++ b/drivers/staging/wfx/data_tx.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include + +#include "data_tx.h" +#include "wfx.h" +#include "bh.h" +#include "queue.h" +#include "debug.h" +#include "traces.h" +#include "hif_tx_mib.h" + +#define WFX_INVALID_RATE_ID (0xFF) +#define WFX_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +static int wfx_get_hw_rate(struct wfx_dev *wdev, const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return -1; + if (rate->flags & IEEE80211_TX_RC_MCS) { + if (rate->idx > 7) { + WARN(1, "wrong rate->idx value: %d", rate->idx); + return -1; + } + return rate->idx + 14; + } + // WFx only support 2GHz, else band information should be retreived + // from ieee80211_tx_info + return wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->bitrates[rate->idx].hw_value; +} + +/* TX policy cache implementation */ + +static void tx_policy_build(struct wfx_vif *wvif, struct tx_policy *policy, + struct ieee80211_tx_rate *rates) +{ + int i; + size_t count; + struct wfx_dev *wdev = wvif->wdev; + + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) + if (rates[i].idx < 0) + break; + count = i; + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more + */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + int rateid; + uint8_t count; + + if (rates[i].idx < 0) + break; + WARN_ON(rates[i].count > 15); + rateid = wfx_get_hw_rate(wdev, &rates[i]); + // Pack two values in each byte of policy->rates + count = rates[i].count; + if (rateid % 2) + count <<= 4; + policy->rates[rateid / 2] |= count; + } +} + +static bool tx_policy_is_equal(const struct tx_policy *a, const struct tx_policy *b) +{ + return !memcmp(a->rates, b->rates, sizeof(a->rates)); +} + +static int tx_policy_find(struct tx_policy_cache *cache, struct tx_policy *wanted) +{ + struct tx_policy *it; + + list_for_each_entry(it, &cache->used, link) + if (tx_policy_is_equal(wanted, it)) + return it - cache->cache; + list_for_each_entry(it, &cache->free, link) + if (tx_policy_is_equal(wanted, it)) + return it - cache->cache; + return -1; +} + +static void tx_policy_use(struct tx_policy_cache *cache, struct tx_policy *entry) +{ + ++entry->usage_count; + list_move(&entry->link, &cache->used); +} + +static int tx_policy_release(struct tx_policy_cache *cache, struct tx_policy *entry) +{ + int ret = --entry->usage_count; + + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +static int tx_policy_get(struct wfx_vif *wvif, struct ieee80211_tx_rate *rates, + bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(wvif, &wanted, rates); + + spin_lock_bh(&cache->lock); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return WFX_INVALID_RATE_ID; + } + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + *renew = false; + } else { + struct tx_policy *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list + */ + entry = list_entry(cache->free.prev, struct tx_policy, link); + memcpy(entry->rates, wanted.rates, sizeof(entry->rates)); + entry->uploaded = 0; + entry->usage_count = 0; + idx = entry - cache->cache; + } + tx_policy_use(cache, &cache->cache[idx]); + if (list_empty(&cache->free)) { + /* Lock TX queues. */ + wfx_tx_queues_lock(wvif->wdev); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct wfx_vif *wvif, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (locked && !usage) { + /* Unlock TX queues. */ + wfx_tx_queues_unlock(wvif->wdev); + } + spin_unlock_bh(&cache->lock); +} + +static int tx_policy_upload(struct wfx_vif *wvif) +{ + int i; + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + struct hif_mib_set_tx_rate_retry_policy *arg = + kzalloc(struct_size(arg, tx_rate_retry_policy, HIF_MIB_NUM_TX_RATE_RETRY_POLICIES), GFP_KERNEL); + struct hif_mib_tx_rate_retry_policy *dst; + + spin_lock_bh(&cache->lock); + /* Upload only modified entries. */ + for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i) { + struct tx_policy *src = &cache->cache[i]; + + if (!src->uploaded && memzcmp(src->rates, sizeof(src->rates))) { + dst = arg->tx_rate_retry_policy + arg->num_tx_rate_policies; + + dst->policy_index = i; + dst->short_retry_count = 255; + dst->long_retry_count = 255; + dst->first_rate_sel = 1; + dst->terminate = 1; + dst->count_init = 1; + memcpy(&dst->rates, src->rates, sizeof(src->rates)); + src->uploaded = 1; + arg->num_tx_rate_policies++; + } + } + spin_unlock_bh(&cache->lock); + hif_set_tx_rate_retry_policy(wvif, arg); + kfree(arg); + return 0; +} + +static void tx_policy_upload_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, tx_policy_upload_work); + + tx_policy_upload(wvif); + + wfx_tx_unlock(wvif->wdev); + wfx_tx_queues_unlock(wvif->wdev); +} + +void tx_policy_init(struct wfx_vif *wvif) +{ + struct tx_policy_cache *cache = &wvif->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + INIT_WORK(&wvif->tx_policy_upload_work, tx_policy_upload_work); + + for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +/* Link ID related functions */ + +static int wfx_alloc_link_id(struct wfx_vif *wvif, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + if (!wvif->link_id_db[i].status) { + ret = i + 1; + break; + } else if (wvif->link_id_db[i].status != WFX_LINK_HARD && + !wvif->wdev->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = + now - wvif->link_id_db[i].timestamp; + + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + + if (ret) { + struct wfx_link_entry *entry = &wvif->link_id_db[ret - 1]; + + entry->status = WFX_LINK_RESERVE; + ether_addr_copy(entry->mac, mac); + memset(&entry->buffered, 0, WFX_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wfx_tx_lock(wvif->wdev); + + if (!schedule_work(&wvif->link_id_work)) + wfx_tx_unlock(wvif->wdev); + } else { + dev_info(wvif->wdev->dev, "no more link-id available\n"); + } + spin_unlock_bh(&wvif->ps_state_lock); + return ret; +} + +int wfx_find_link_id(struct wfx_vif *wvif, const u8 *mac) +{ + int i, ret = 0; + + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + if (ether_addr_equal(mac, wvif->link_id_db[i].mac) && + wvif->link_id_db[i].status) { + wvif->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&wvif->ps_state_lock); + return ret; +} + +static int wfx_map_link(struct wfx_vif *wvif, struct wfx_link_entry *link_entry, int sta_id) +{ + int ret; + + ret = hif_map_link(wvif, link_entry->mac, 0, sta_id); + + if (ret == 0) + /* Save the MAC address currently associated with the peer + * for future unmap request + */ + ether_addr_copy(link_entry->old_mac, link_entry->mac); + + return ret; +} + +int wfx_unmap_link(struct wfx_vif *wvif, int sta_id) +{ + u8 *mac_addr = NULL; + + if (sta_id) + mac_addr = wvif->link_id_db[sta_id - 1].old_mac; + + return hif_map_link(wvif, mac_addr, 1, sta_id); +} + +void wfx_link_id_gc_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, link_id_gc_work.work); + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + u32 mask; + int i; + + wfx_tx_lock_flush(wvif->wdev); + spin_lock_bh(&wvif->ps_state_lock); + for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { + bool need_reset = false; + + mask = BIT(i + 1); + if (wvif->link_id_db[i].status == WFX_LINK_RESERVE || + (wvif->link_id_db[i].status == WFX_LINK_HARD && + !(wvif->link_id_map & mask))) { + if (wvif->link_id_map & mask) { + wvif->sta_asleep_mask &= ~mask; + wvif->pspoll_mask &= ~mask; + need_reset = true; + } + wvif->link_id_map |= mask; + if (wvif->link_id_db[i].status != WFX_LINK_HARD) + wvif->link_id_db[i].status = WFX_LINK_SOFT; + + spin_unlock_bh(&wvif->ps_state_lock); + if (need_reset) + wfx_unmap_link(wvif, i + 1); + wfx_map_link(wvif, &wvif->link_id_db[i], i + 1); + next_gc = min(next_gc, WFX_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&wvif->ps_state_lock); + } else if (wvif->link_id_db[i].status == WFX_LINK_SOFT) { + ttl = wvif->link_id_db[i].timestamp - now + + WFX_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + wvif->link_id_db[i].status = WFX_LINK_OFF; + wvif->link_id_map &= ~mask; + wvif->sta_asleep_mask &= ~mask; + wvif->pspoll_mask &= ~mask; + spin_unlock_bh(&wvif->ps_state_lock); + wfx_unmap_link(wvif, i + 1); + spin_lock_bh(&wvif->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } + } + if (need_reset) + skb_queue_purge(&wvif->link_id_db[i].rx_queue); + } + spin_unlock_bh(&wvif->ps_state_lock); + if (next_gc != -1) + schedule_delayed_work(&wvif->link_id_gc_work, next_gc); + wfx_tx_unlock(wvif->wdev); +} + +void wfx_link_id_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, link_id_work); + + wfx_tx_flush(wvif->wdev); + wfx_link_id_gc_work(&wvif->link_id_gc_work.work); + wfx_tx_unlock(wvif->wdev); +} + +/* Tx implementation */ + +static bool ieee80211_is_action_back(struct ieee80211_hdr *hdr) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) hdr; + + if (!ieee80211_is_action(mgmt->frame_control)) + return false; + if (mgmt->u.action.category != WLAN_CATEGORY_BACK) + return false; + return true; +} + +static void wfx_tx_manage_pm(struct wfx_vif *wvif, struct ieee80211_hdr *hdr, + struct wfx_tx_priv *tx_priv, struct ieee80211_sta *sta) +{ + u32 mask = ~BIT(tx_priv->raw_link_id); + + spin_lock_bh(&wvif->ps_state_lock); + if (ieee80211_is_auth(hdr->frame_control)) { + wvif->sta_asleep_mask &= mask; + wvif->pspoll_mask &= mask; + } + + if (tx_priv->link_id == WFX_LINK_ID_AFTER_DTIM && !wvif->mcast_buffered) { + wvif->mcast_buffered = true; + if (wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_start_work); + } + + if (tx_priv->raw_link_id) { + wvif->link_id_db[tx_priv->raw_link_id - 1].timestamp = jiffies; + if (tx_priv->tid < WFX_MAX_TID) + wvif->link_id_db[tx_priv->raw_link_id - 1].buffered[tx_priv->tid]++; + } + spin_unlock_bh(&wvif->ps_state_lock); + + if (sta) + ieee80211_sta_set_buffered(sta, tx_priv->tid, true); +} + +static uint8_t wfx_tx_get_raw_link_id(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct ieee80211_hdr *hdr) +{ + struct wfx_sta_priv *sta_priv = sta ? (struct wfx_sta_priv *) &sta->drv_priv : NULL; + const u8 *da = ieee80211_get_DA(hdr); + int ret; + + if (sta_priv && sta_priv->link_id) + return sta_priv->link_id; + if (wvif->vif->type != NL80211_IFTYPE_AP) + return 0; + if (is_multicast_ether_addr(da)) + return 0; + ret = wfx_find_link_id(wvif, da); + if (!ret) + ret = wfx_alloc_link_id(wvif, da); + if (!ret) { + dev_err(wvif->wdev->dev, "no more link-id available\n"); + return -ENOENT; + } + return ret; +} + +static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates) +{ + int i; + bool finished; + + // Firmware is not able to mix rates with differents flags + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + rates[i].flags |= IEEE80211_TX_RC_SHORT_GI; + if (!(rates[0].flags & IEEE80211_TX_RC_SHORT_GI)) + rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI; + if (!(rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)) + rates[i].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; + } + + // Sort rates and remove duplicates + do { + finished = true; + for (i = 0; i < IEEE80211_TX_MAX_RATES - 1; i++) { + if (rates[i + 1].idx == rates[i].idx && rates[i].idx != -1) { + rates[i].count = max_t(int, rates[i].count, rates[i + 1].count); + rates[i + 1].idx = -1; + rates[i + 1].count = 0; + + finished = false; + } + if (rates[i + 1].idx > rates[i].idx) { + swap(rates[i + 1], rates[i]); + finished = false; + } + } + } while (!finished); + // All retries use long GI + for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) + rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI; +} + +static uint8_t wfx_tx_get_rate_id(struct wfx_vif *wvif, struct ieee80211_tx_info *tx_info) +{ + bool tx_policy_renew = false; + uint8_t rate_id; + + rate_id = tx_policy_get(wvif, tx_info->driver_rates, &tx_policy_renew); + WARN(rate_id == WFX_INVALID_RATE_ID, "unable to get a valid Tx policy"); + + if (tx_policy_renew) { + /* FIXME: It's not so optimal to stop TX queues every now and + * then. Better to reimplement task scheduling with a counter. + */ + wfx_tx_lock(wvif->wdev); + wfx_tx_queues_lock(wvif->wdev); + if (!schedule_work(&wvif->tx_policy_upload_work)) { + wfx_tx_queues_unlock(wvif->wdev); + wfx_tx_unlock(wvif->wdev); + } + } + return rate_id; +} + +static struct hif_ht_tx_parameters wfx_tx_get_tx_parms(struct wfx_dev *wdev, struct ieee80211_tx_info *tx_info) +{ + struct ieee80211_tx_rate *rate = &tx_info->driver_rates[0]; + struct hif_ht_tx_parameters ret = { }; + + if (!(rate->flags & IEEE80211_TX_RC_MCS)) + ret.frame_format = HIF_FRAME_FORMAT_NON_HT; + else if (!(rate->flags & IEEE80211_TX_RC_GREEN_FIELD)) + ret.frame_format = HIF_FRAME_FORMAT_MIXED_FORMAT_HT; + else + ret.frame_format = HIF_FRAME_FORMAT_GF_HT_11N; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + ret.short_gi = 1; + if (tx_info->flags & IEEE80211_TX_CTL_STBC) + ret.stbc = 0; // FIXME: Not yet supported by firmware? + return ret; +} + +static uint8_t wfx_tx_get_tid(struct ieee80211_hdr *hdr) +{ + // FIXME: ieee80211_get_tid(hdr) should be sufficient for all cases. + if (!ieee80211_is_data(hdr->frame_control)) + return WFX_MAX_TID; + if (ieee80211_is_data_qos(hdr->frame_control)) + return ieee80211_get_tid(hdr); + else + return 0; +} + +static int wfx_tx_get_icv_len(struct ieee80211_key_conf *hw_key) +{ + int mic_space; + + if (!hw_key) + return 0; + mic_space = (hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) ? 8 : 0; + return hw_key->icv_len + mic_space; +} + +static int wfx_tx_inner(struct wfx_vif *wvif, struct ieee80211_sta *sta, struct sk_buff *skb) +{ + struct hif_msg *hif_msg; + struct hif_req_tx *req; + struct wfx_tx_priv *tx_priv; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int queue_id = tx_info->hw_queue; + size_t offset = (size_t) skb->data & 3; + int wmsg_len = sizeof(struct hif_msg) + sizeof(struct hif_req_tx) + offset; + + WARN(queue_id >= IEEE80211_NUM_ACS, "unsupported queue_id"); + wfx_tx_fixup_rates(tx_info->driver_rates); + + // From now tx_info->control is unusable + memset(tx_info->rate_driver_data, 0, sizeof(struct wfx_tx_priv)); + // Fill tx_priv + tx_priv = (struct wfx_tx_priv *) tx_info->rate_driver_data; + tx_priv->tid = wfx_tx_get_tid(hdr); + tx_priv->raw_link_id = wfx_tx_get_raw_link_id(wvif, sta, hdr); + tx_priv->link_id = tx_priv->raw_link_id; + if (ieee80211_has_protected(hdr->frame_control)) + tx_priv->hw_key = hw_key; + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) + tx_priv->link_id = WFX_LINK_ID_AFTER_DTIM; + if (sta && (sta->uapsd_queues & BIT(queue_id))) + tx_priv->link_id = WFX_LINK_ID_UAPSD; + + // Fill hif_msg + WARN(skb_headroom(skb) < wmsg_len, "not enough space in skb"); + WARN(offset & 1, "attempt to transmit an unaligned frame"); + skb_put(skb, wfx_tx_get_icv_len(tx_priv->hw_key)); + skb_push(skb, wmsg_len); + memset(skb->data, 0, wmsg_len); + hif_msg = (struct hif_msg *) skb->data; + hif_msg->len = cpu_to_le16(skb->len); + hif_msg->id = cpu_to_le16(HIF_REQ_ID_TX); + hif_msg->interface = wvif->id; + if (skb->len > wvif->wdev->hw_caps.size_inp_ch_buf) { + dev_warn(wvif->wdev->dev, "requested frame size (%d) is larger than maximum supported (%d)\n", + skb->len, wvif->wdev->hw_caps.size_inp_ch_buf); + skb_pull(skb, wmsg_len); + return -EIO; + } + + // Fill tx request + req = (struct hif_req_tx *) hif_msg->body; + req->packet_id = queue_id << 16 | IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + req->data_flags.fc_offset = offset; + req->queue_id.peer_sta_id = tx_priv->raw_link_id; + // Queue index are inverted between firmware and Linux + req->queue_id.queue_id = 3 - queue_id; + req->ht_tx_parameters = wfx_tx_get_tx_parms(wvif->wdev, tx_info); + req->tx_flags.retry_policy_index = wfx_tx_get_rate_id(wvif, tx_info); + + // Auxilliary operations + wfx_tx_manage_pm(wvif, hdr, tx_priv, sta); + wfx_tx_queue_put(wvif->wdev, &wvif->wdev->tx_queue[queue_id], skb); + wfx_bh_request_tx(wvif->wdev); + return 0; +} + +void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif; + struct ieee80211_sta *sta = control ? control->sta : NULL; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + size_t driver_data_room = FIELD_SIZEOF(struct ieee80211_tx_info, rate_driver_data); + + compiletime_assert(sizeof(struct wfx_tx_priv) <= driver_data_room, + "struct tx_priv is too large"); + WARN(skb->next || skb->prev, "skb is already member of a list"); + // control.vif can be NULL for injected frames + if (tx_info->control.vif) + wvif = (struct wfx_vif *) tx_info->control.vif->drv_priv; + else + wvif = wvif_iterate(wdev, NULL); + if (WARN_ON(!wvif)) + goto drop; + // FIXME: why? + if (ieee80211_is_action_back(hdr)) { + dev_info(wdev->dev, "drop BA action\n"); + goto drop; + } + if (wfx_tx_inner(wvif, sta, skb)) + goto drop; + + return; + +drop: + ieee80211_tx_status_irqsafe(wdev->hw, skb); +} + +void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg) +{ + int i; + int tx_count; + struct sk_buff *skb; + struct ieee80211_tx_rate *rate; + struct ieee80211_tx_info *tx_info; + const struct wfx_tx_priv *tx_priv; + + skb = wfx_pending_get(wvif->wdev, arg->packet_id); + if (!skb) { + dev_warn(wvif->wdev->dev, "received unknown packet_id (%#.8x) from chip\n", arg->packet_id); + return; + } + tx_info = IEEE80211_SKB_CB(skb); + tx_priv = wfx_skb_tx_priv(skb); + _trace_tx_stats(arg, skb, wfx_pending_get_pkt_us_delay(wvif->wdev, skb)); + + // You can touch to tx_priv, but don't touch to tx_info->status. + tx_count = arg->ack_failures; + if (!arg->status || arg->ack_failures) + tx_count += 1; // Also report success + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + rate = &tx_info->status.rates[i]; + if (rate->idx < 0) + break; + if (tx_count < rate->count && arg->status && arg->ack_failures) + dev_dbg(wvif->wdev->dev, "all retries were not consumed: %d != %d\n", + rate->count, tx_count); + if (tx_count <= rate->count && tx_count && arg->txed_rate != wfx_get_hw_rate(wvif->wdev, rate)) + dev_dbg(wvif->wdev->dev, "inconsistent tx_info rates: %d != %d\n", + arg->txed_rate, wfx_get_hw_rate(wvif->wdev, rate)); + if (tx_count > rate->count) { + tx_count -= rate->count; + } else if (!tx_count) { + rate->count = 0; + rate->idx = -1; + } else { + rate->count = tx_count; + tx_count = 0; + } + } + if (tx_count) + dev_dbg(wvif->wdev->dev, "%d more retries than expected\n", tx_count); + skb_trim(skb, skb->len - wfx_tx_get_icv_len(tx_priv->hw_key)); + + // From now, you can touch to tx_info->status, but do not touch to + // tx_priv anymore + // FIXME: use ieee80211_tx_info_clear_status() + memset(tx_info->rate_driver_data, 0, sizeof(tx_info->rate_driver_data)); + memset(tx_info->pad, 0, sizeof(tx_info->pad)); + + if (!arg->status) { + tx_info->status.tx_time = arg->media_delay - arg->tx_queue_delay; + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } else if (arg->status == HIF_REQUEUE) { + WARN(!arg->tx_result_flags.requeue, "incoherent status and result_flags"); + tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + } + wfx_pending_remove(wvif->wdev, skb); +} + +static void wfx_notify_buffered_tx(struct wfx_vif *wvif, struct sk_buff *skb, + struct hif_req_tx *req) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int tid = wfx_tx_get_tid(hdr); + int raw_link_id = req->queue_id.peer_sta_id; + u8 *buffered; + + if (raw_link_id && tid < WFX_MAX_TID) { + buffered = wvif->link_id_db[raw_link_id - 1].buffered; + + spin_lock_bh(&wvif->ps_state_lock); + WARN(!buffered[tid], "inconsistent notification"); + buffered[tid]--; + spin_unlock_bh(&wvif->ps_state_lock); + + if (!buffered[tid]) { + rcu_read_lock(); + sta = ieee80211_find_sta(wvif->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +} + +void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct hif_msg *hif = (struct hif_msg *) skb->data; + struct hif_req_tx *req = (struct hif_req_tx *) hif->body; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + unsigned int offset = sizeof(struct hif_req_tx) + sizeof(struct hif_msg) + req->data_flags.fc_offset; + + WARN_ON(!wvif); + skb_pull(skb, offset); + wfx_notify_buffered_tx(wvif, skb, req); + tx_policy_put(wvif, req->tx_flags.retry_policy_index); + ieee80211_tx_status_irqsafe(wdev->hw, skb); +} diff --git a/drivers/staging/wfx/data_tx.h b/drivers/staging/wfx/data_tx.h new file mode 100644 index 000000000000..f59a259bb744 --- /dev/null +++ b/drivers/staging/wfx/data_tx.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_DATA_TX_H +#define WFX_DATA_TX_H + +#include +#include + +#include "hif_api_cmd.h" +#include "hif_api_mib.h" + +// FIXME: use IEEE80211_NUM_TIDS +#define WFX_MAX_TID 8 + +struct wfx_tx_priv; +struct wfx_dev; +struct wfx_vif; + +enum wfx_link_status { + WFX_LINK_OFF, + WFX_LINK_RESERVE, + WFX_LINK_SOFT, + WFX_LINK_HARD, +}; + +struct wfx_link_entry { + unsigned long timestamp; + enum wfx_link_status status; + uint8_t mac[ETH_ALEN]; + uint8_t old_mac[ETH_ALEN]; + uint8_t buffered[WFX_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct tx_policy { + struct list_head link; + uint8_t rates[12]; + uint8_t usage_count; + uint8_t uploaded; +}; + +struct tx_policy_cache { + struct tx_policy cache[HIF_MIB_NUM_TX_RATE_RETRY_POLICIES]; + // FIXME: use a trees and drop hash from tx_policy + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +struct wfx_tx_priv { + ktime_t xmit_timestamp; + struct ieee80211_key_conf *hw_key; + uint8_t link_id; + uint8_t raw_link_id; + uint8_t tid; +} __packed; + +void tx_policy_init(struct wfx_vif *wvif); + +void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg); +void wfx_skb_dtor(struct wfx_dev *wdev, struct sk_buff *skb); + +int wfx_unmap_link(struct wfx_vif *wvif, int link_id); +void wfx_link_id_work(struct work_struct *work); +void wfx_link_id_gc_work(struct work_struct *work); +int wfx_find_link_id(struct wfx_vif *wvif, const u8 *mac); + +static inline struct wfx_tx_priv *wfx_skb_tx_priv(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info; + + if (!skb) + return NULL; + tx_info = IEEE80211_SKB_CB(skb); + return (struct wfx_tx_priv *) tx_info->rate_driver_data; +} + +static inline struct hif_req_tx *wfx_skb_txreq(struct sk_buff *skb) +{ + struct hif_msg *hif = (struct hif_msg *) skb->data; + struct hif_req_tx *req = (struct hif_req_tx *) hif->body; + + return req; +} + +#endif /* WFX_DATA_TX_H */ diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index c93bae1b6acf..97c4c2f082fb 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -53,6 +53,39 @@ static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void * return status; } +static int hif_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_cnf_tx *body = buf; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + if (!wvif) + return -EFAULT; + + wfx_tx_confirm_cb(wvif, body); + return 0; +} + +static int hif_multi_tx_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct hif_cnf_multi_transmit *body = buf; + struct hif_cnf_tx *buf_loc = (struct hif_cnf_tx *) &body->tx_conf_payload; + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + int count = body->num_tx_confs; + int i; + + WARN(count <= 0, "corrupted message"); + WARN_ON(!wvif); + if (!wvif) + return -EFAULT; + + for (i = 0; i < count; ++i) { + wfx_tx_confirm_cb(wvif, buf_loc); + buf_loc++; + } + return 0; +} + static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct hif_ind_startup *body = buf; @@ -174,6 +207,10 @@ static const struct { int msg_id; int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf); } hif_handlers[] = { + /* Confirmations */ + { HIF_CNF_ID_TX, hif_tx_confirm }, + { HIF_CNF_ID_MULTI_TRANSMIT, hif_multi_tx_confirm }, + /* Indications */ { HIF_IND_ID_STARTUP, hif_startup_indication }, { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication }, diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c index f8ab871aa188..157ab177b73f 100644 --- a/drivers/staging/wfx/hif_tx.c +++ b/drivers/staging/wfx/hif_tx.c @@ -88,6 +88,7 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz } if (!ret) { dev_err(wdev->dev, "chip did not answer\n"); + wfx_pending_dump_old_frames(wdev, 3000); wdev->chip_frozen = 1; reinit_completion(&wdev->hif_cmd.done); ret = -ETIMEDOUT; diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 2e71f446d4d4..cce4e30dd94a 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -28,6 +28,7 @@ #include "bh.h" #include "sta.h" #include "debug.h" +#include "data_tx.h" #include "secure_link.h" #include "hif_tx_mib.h" #include "hif_api_cmd.h" @@ -53,6 +54,7 @@ static const struct ieee80211_ops wfx_ops = { .stop = wfx_stop, .add_interface = wfx_add_interface, .remove_interface = wfx_remove_interface, + .tx = wfx_tx, }; bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) @@ -215,6 +217,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, mutex_init(&wdev->rx_stats_lock); init_completion(&wdev->firmware_ready); wfx_init_hif_cmd(&wdev->hif_cmd); + wfx_tx_queues_init(wdev); return wdev; } @@ -222,6 +225,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, void wfx_free_common(struct wfx_dev *wdev) { mutex_destroy(&wdev->rx_stats_lock); + wfx_tx_queues_deinit(wdev); ieee80211_free_hw(wdev->hw); } diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c new file mode 100644 index 000000000000..aa438be21d37 --- /dev/null +++ b/drivers/staging/wfx/queue.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * O(1) TX queue with built-in allocator. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "queue.h" +#include "wfx.h" +#include "sta.h" +#include "data_tx.h" + +void wfx_tx_lock(struct wfx_dev *wdev) +{ + atomic_inc(&wdev->tx_lock); +} + +void wfx_tx_unlock(struct wfx_dev *wdev) +{ + int tx_lock = atomic_dec_return(&wdev->tx_lock); + + WARN(tx_lock < 0, "inconsistent tx_lock value"); + if (!tx_lock) + wfx_bh_request_tx(wdev); +} + +void wfx_tx_flush(struct wfx_dev *wdev) +{ + int ret; + + WARN(!atomic_read(&wdev->tx_lock), "tx_lock is not locked"); + + // Do not wait for any reply if chip is frozen + if (wdev->chip_frozen) + return; + + mutex_lock(&wdev->hif_cmd.lock); + ret = wait_event_timeout(wdev->hif.tx_buffers_empty, + !wdev->hif.tx_buffers_used, + msecs_to_jiffies(3000)); + if (!ret) { + dev_warn(wdev->dev, "cannot flush tx buffers (%d still busy)\n", wdev->hif.tx_buffers_used); + wfx_pending_dump_old_frames(wdev, 3000); + // FIXME: drop pending frames here + wdev->chip_frozen = 1; + } + mutex_unlock(&wdev->hif_cmd.lock); +} + +void wfx_tx_lock_flush(struct wfx_dev *wdev) +{ + wfx_tx_lock(wdev); + wfx_tx_flush(wdev); +} + +void wfx_tx_queues_lock(struct wfx_dev *wdev) +{ + int i; + struct wfx_queue *queue; + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + if (queue->tx_locked_cnt++ == 0) + ieee80211_stop_queue(wdev->hw, queue->queue_id); + spin_unlock_bh(&queue->queue.lock); + } +} + +void wfx_tx_queues_unlock(struct wfx_dev *wdev) +{ + int i; + struct wfx_queue *queue; + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) + ieee80211_wake_queue(wdev->hw, queue->queue_id); + spin_unlock_bh(&queue->queue.lock); + } +} + +/* If successful, LOCKS the TX queue! */ +void wfx_tx_queues_wait_empty_vif(struct wfx_vif *wvif) +{ + int i; + bool done; + struct wfx_queue *queue; + struct sk_buff *item; + struct wfx_dev *wdev = wvif->wdev; + struct hif_msg *hif; + + if (wvif->wdev->chip_frozen) { + wfx_tx_lock_flush(wdev); + wfx_tx_queues_clear(wdev); + return; + } + + do { + done = true; + wfx_tx_lock_flush(wdev); + for (i = 0; i < IEEE80211_NUM_ACS && done; ++i) { + queue = &wdev->tx_queue[i]; + spin_lock_bh(&queue->queue.lock); + skb_queue_walk(&queue->queue, item) { + hif = (struct hif_msg *) item->data; + if (hif->interface == wvif->id) + done = false; + } + spin_unlock_bh(&queue->queue.lock); + } + if (!done) { + wfx_tx_unlock(wdev); + msleep(20); + } + } while (!done); +} + +static void wfx_tx_queue_clear(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff_head *gc_list) +{ + int i; + struct sk_buff *item; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&queue->queue.lock); + while ((item = __skb_dequeue(&queue->queue)) != NULL) + skb_queue_head(gc_list, item); + spin_lock_bh(&stats->pending.lock); + for (i = 0; i < ARRAY_SIZE(stats->link_map_cache); ++i) { + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->pending.lock); + spin_unlock_bh(&queue->queue.lock); +} + +void wfx_tx_queues_clear(struct wfx_dev *wdev) +{ + int i; + struct sk_buff *item; + struct sk_buff_head gc_list; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + skb_queue_head_init(&gc_list); + for (i = 0; i < IEEE80211_NUM_ACS; ++i) + wfx_tx_queue_clear(wdev, &wdev->tx_queue[i], &gc_list); + wake_up(&stats->wait_link_id_empty); + while ((item = skb_dequeue(&gc_list)) != NULL) + wfx_skb_dtor(wdev, item); +} + +void wfx_tx_queues_init(struct wfx_dev *wdev) +{ + int i; + + memset(&wdev->tx_queue_stats, 0, sizeof(wdev->tx_queue_stats)); + memset(wdev->tx_queue, 0, sizeof(wdev->tx_queue)); + skb_queue_head_init(&wdev->tx_queue_stats.pending); + init_waitqueue_head(&wdev->tx_queue_stats.wait_link_id_empty); + + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + wdev->tx_queue[i].queue_id = i; + skb_queue_head_init(&wdev->tx_queue[i].queue); + } +} + +void wfx_tx_queues_deinit(struct wfx_dev *wdev) +{ + WARN_ON(!skb_queue_empty(&wdev->tx_queue_stats.pending)); + wfx_tx_queues_clear(wdev); +} + +size_t wfx_tx_queue_get_num_queued(struct wfx_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->queue.lock); + if (link_id_map == (u32)-1) { + ret = skb_queue_len(&queue->queue); + } else { + ret = 0; + for (i = 0, bit = 1; i < ARRAY_SIZE(queue->link_map_cache); ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->queue.lock); + return ret; +} + +void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + + WARN(tx_priv->link_id >= ARRAY_SIZE(stats->link_map_cache), "invalid link-id value"); + spin_lock_bh(&queue->queue.lock); + __skb_queue_tail(&queue->queue, skb); + + ++queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + ++stats->link_map_cache[tx_priv->link_id]; + spin_unlock_bh(&stats->pending.lock); + spin_unlock_bh(&queue->queue.lock); +} + +struct sk_buff *wfx_tx_queue_get(struct wfx_dev *wdev, struct wfx_queue *queue, u32 link_id_map) +{ + struct sk_buff *skb = NULL; + struct sk_buff *item; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv; + bool wakeup_stats = false; + + spin_lock_bh(&queue->queue.lock); + skb_queue_walk(&queue->queue, item) { + tx_priv = wfx_skb_tx_priv(item); + if (link_id_map & BIT(tx_priv->link_id)) { + skb = item; + break; + } + } + WARN_ON(!skb); + if (skb) { + tx_priv = wfx_skb_tx_priv(skb); + tx_priv->xmit_timestamp = ktime_get(); + __skb_unlink(skb, &queue->queue); + --queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + __skb_queue_tail(&stats->pending, skb); + if (!--stats->link_map_cache[tx_priv->link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->pending.lock); + } + spin_unlock_bh(&queue->queue.lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return skb; +} + +int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + struct wfx_queue *queue = &wdev->tx_queue[skb_get_queue_mapping(skb)]; + + WARN_ON(skb_get_queue_mapping(skb) > 3); + spin_lock_bh(&queue->queue.lock); + ++queue->link_map_cache[tx_priv->link_id]; + + spin_lock_bh(&stats->pending.lock); + ++stats->link_map_cache[tx_priv->link_id]; + __skb_unlink(skb, &stats->pending); + spin_unlock_bh(&stats->pending.lock); + __skb_queue_tail(&queue->queue, skb); + spin_unlock_bh(&queue->queue.lock); + return 0; +} + +int wfx_pending_remove(struct wfx_dev *wdev, struct sk_buff *skb) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&stats->pending.lock); + __skb_unlink(skb, &stats->pending); + spin_unlock_bh(&stats->pending.lock); + wfx_skb_dtor(wdev, skb); + + return 0; +} + +struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id) +{ + struct sk_buff *skb; + struct hif_req_tx *req; + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + + spin_lock_bh(&stats->pending.lock); + skb_queue_walk(&stats->pending, skb) { + req = wfx_skb_txreq(skb); + if (req->packet_id == packet_id) { + spin_unlock_bh(&stats->pending.lock); + return skb; + } + } + WARN_ON(1); + spin_unlock_bh(&stats->pending.lock); + return NULL; +} + +void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms) +{ + struct wfx_queue_stats *stats = &wdev->tx_queue_stats; + ktime_t now = ktime_get(); + struct wfx_tx_priv *tx_priv; + struct hif_req_tx *req; + struct sk_buff *skb; + bool first = true; + + spin_lock_bh(&stats->pending.lock); + skb_queue_walk(&stats->pending, skb) { + tx_priv = wfx_skb_tx_priv(skb); + req = wfx_skb_txreq(skb); + if (ktime_after(now, ktime_add_ms(tx_priv->xmit_timestamp, limit_ms))) { + if (first) { + dev_info(wdev->dev, "frames stuck in firmware since %dms or more:\n", + limit_ms); + first = false; + } + dev_info(wdev->dev, " id %08x sent %lldms ago\n", + req->packet_id, + ktime_ms_delta(now, tx_priv->xmit_timestamp)); + } + } + spin_unlock_bh(&stats->pending.lock); +} + +unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb) +{ + ktime_t now = ktime_get(); + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + + return ktime_us_delta(now, tx_priv->xmit_timestamp); +} + +bool wfx_tx_queues_is_empty(struct wfx_dev *wdev) +{ + int i; + struct sk_buff_head *queue; + bool ret = true; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + queue = &wdev->tx_queue[i].queue; + spin_lock_bh(&queue->lock); + if (!skb_queue_empty(queue)) + ret = false; + spin_unlock_bh(&queue->lock); + } + return ret; +} + +static int wfx_get_prio_queue(struct wfx_vif *wvif, + u32 tx_allowed_mask, int *total) +{ + static const int urgent = BIT(WFX_LINK_ID_AFTER_DTIM) | + BIT(WFX_LINK_ID_UAPSD); + struct hif_req_edca_queue_params *edca; + unsigned int score, best = -1; + int winner = -1; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + int queued; + + edca = &wvif->edca.params[i]; + queued = wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[i], + tx_allowed_mask); + if (!queued) + continue; + *total += queued; + score = ((edca->aifsn + edca->cw_min) << 16) + + ((edca->cw_max - edca->cw_min) * + (get_random_int() & 0xFFFF)); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && wvif->wdev->tx_burst_idx >= 0 && + winner != wvif->wdev->tx_burst_idx && + !wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[winner], tx_allowed_mask & urgent) && + wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[wvif->wdev->tx_burst_idx], tx_allowed_mask)) + winner = wvif->wdev->tx_burst_idx; + + return winner; +} + +static int wfx_tx_queue_mask_get(struct wfx_vif *wvif, + struct wfx_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (wvif->mcast_tx) { + tx_allowed_mask = BIT(WFX_LINK_ID_AFTER_DTIM); + idx = wfx_get_prio_queue(wvif, tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~wvif->sta_asleep_mask; + tx_allowed_mask |= BIT(WFX_LINK_ID_UAPSD); + if (wvif->sta_asleep_mask) { + tx_allowed_mask |= wvif->pspoll_mask; + tx_allowed_mask &= ~BIT(WFX_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(WFX_LINK_ID_AFTER_DTIM); + } + idx = wfx_get_prio_queue(wvif, tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &wvif->wdev->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev) +{ + struct sk_buff *skb; + struct hif_msg *hif = NULL; + struct hif_req_tx *req = NULL; + struct wfx_queue *queue = NULL; + struct wfx_queue *vif_queue = NULL; + u32 tx_allowed_mask = 0; + u32 vif_tx_allowed_mask = 0; + const struct wfx_tx_priv *tx_priv = NULL; + struct wfx_vif *wvif; + /* More is used only for broadcasts. */ + bool more = false; + bool vif_more = false; + int not_found; + int burst; + + for (;;) { + int ret = -ENOENT; + int queue_num; + struct ieee80211_hdr *hdr; + + if (atomic_read(&wdev->tx_lock)) + return NULL; + + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + spin_lock_bh(&wvif->ps_state_lock); + + not_found = wfx_tx_queue_mask_get(wvif, &vif_queue, &vif_tx_allowed_mask, &vif_more); + + if (wvif->mcast_buffered && (not_found || !vif_more) && + (wvif->mcast_tx || !wvif->sta_asleep_mask)) { + wvif->mcast_buffered = false; + if (wvif->mcast_tx) { + wvif->mcast_tx = false; + schedule_work(&wvif->mcast_stop_work); + } + } + + spin_unlock_bh(&wvif->ps_state_lock); + + if (vif_more) { + more = 1; + tx_allowed_mask = vif_tx_allowed_mask; + queue = vif_queue; + ret = 0; + break; + } else if (!not_found) { + if (queue && queue != vif_queue) + dev_info(wdev->dev, "vifs disagree about queue priority\n"); + tx_allowed_mask |= vif_tx_allowed_mask; + queue = vif_queue; + ret = 0; + } + } + + if (ret) + return 0; + + queue_num = queue - wdev->tx_queue; + + skb = wfx_tx_queue_get(wdev, queue, tx_allowed_mask); + if (!skb) + continue; + tx_priv = wfx_skb_tx_priv(skb); + hif = (struct hif_msg *) skb->data; + wvif = wdev_to_wvif(wdev, hif->interface); + WARN_ON(!wvif); + + wvif->pspoll_mask &= ~BIT(tx_priv->raw_link_id); + + /* allow bursting if txop is set */ + if (wvif->edca.params[queue_num].tx_op_limit) + burst = (int)wfx_tx_queue_get_num_queued(queue, tx_allowed_mask) + 1; + else + burst = 1; + + /* store index of bursting queue */ + if (burst > 1) + wdev->tx_burst_idx = queue_num; + else + wdev->tx_burst_idx = -1; + + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs + */ + if (more) { + req = (struct hif_req_tx *) hif->body; + hdr = (struct ieee80211_hdr *) (req->frame + req->data_flags.fc_offset); + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + return hif; + } +} diff --git a/drivers/staging/wfx/queue.h b/drivers/staging/wfx/queue.h new file mode 100644 index 000000000000..938dbf3469e7 --- /dev/null +++ b/drivers/staging/wfx/queue.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * O(1) TX queue with built-in allocator. + * + * Copyright (c) 2017-2018, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_QUEUE_H +#define WFX_QUEUE_H + +#include + +#include "hif_api_cmd.h" + +#define WFX_MAX_STA_IN_AP_MODE 14 +#define WFX_LINK_ID_AFTER_DTIM (WFX_MAX_STA_IN_AP_MODE + 1) +#define WFX_LINK_ID_UAPSD (WFX_MAX_STA_IN_AP_MODE + 2) +#define WFX_LINK_ID_MAX (WFX_MAX_STA_IN_AP_MODE + 3) + +struct wfx_dev; +struct wfx_vif; + +struct wfx_queue { + struct sk_buff_head queue; + int tx_locked_cnt; + int link_map_cache[WFX_LINK_ID_MAX]; + u8 queue_id; +}; + +struct wfx_queue_stats { + int link_map_cache[WFX_LINK_ID_MAX]; + struct sk_buff_head pending; + wait_queue_head_t wait_link_id_empty; +}; + +void wfx_tx_lock(struct wfx_dev *wdev); +void wfx_tx_unlock(struct wfx_dev *wdev); +void wfx_tx_flush(struct wfx_dev *wdev); +void wfx_tx_lock_flush(struct wfx_dev *wdev); + +void wfx_tx_queues_init(struct wfx_dev *wdev); +void wfx_tx_queues_deinit(struct wfx_dev *wdev); +void wfx_tx_queues_lock(struct wfx_dev *wdev); +void wfx_tx_queues_unlock(struct wfx_dev *wdev); +void wfx_tx_queues_clear(struct wfx_dev *wdev); +bool wfx_tx_queues_is_empty(struct wfx_dev *wdev); +void wfx_tx_queues_wait_empty_vif(struct wfx_vif *wvif); +struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev); + +void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, struct sk_buff *skb); +size_t wfx_tx_queue_get_num_queued(struct wfx_queue *queue, u32 link_id_map); + +struct sk_buff *wfx_pending_get(struct wfx_dev *wdev, u32 packet_id); +int wfx_pending_remove(struct wfx_dev *wdev, struct sk_buff *skb); +int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb); +unsigned int wfx_pending_get_pkt_us_delay(struct wfx_dev *wdev, struct sk_buff *skb); +void wfx_pending_dump_old_frames(struct wfx_dev *wdev, unsigned int limit_ms); + +#endif /* WFX_QUEUE_H */ diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index fe3ff6536a87..1b45be530070 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -5,16 +5,134 @@ * Copyright (c) 2017-2019, Silicon Laboratories, Inc. * Copyright (c) 2010, ST-Ericsson */ +#include #include #include "sta.h" #include "wfx.h" +#define TXOP_UNIT 32 + +static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct hif_ie_flags target_frame = { + .beacon = 1, + }; + u16 tim_offset, tim_length; + u8 *tim_ptr; + + skb = ieee80211_beacon_get_tim(wvif->wdev->hw, wvif->vif, + &tim_offset, &tim_length); + if (!skb) + return -ENOENT; + tim_ptr = skb->data + tim_offset; + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + tim_ptr[2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + tim_ptr[4] |= 1; + else + tim_ptr[4] &= ~1; + } + + hif_update_ie(wvif, &target_frame, tim_ptr, tim_length); + dev_kfree_skb(skb); + + return 0; +} + +static void wfx_mcast_start_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_start_work); + + cancel_work_sync(&wvif->mcast_stop_work); + if (!wvif->aid0_bit_set) { + wfx_tx_lock_flush(wvif->wdev); + wfx_set_tim_impl(wvif, true); + wvif->aid0_bit_set = true; + mod_timer(&wvif->mcast_timeout, TU_TO_JIFFIES(1000)); + wfx_tx_unlock(wvif->wdev); + } +} + +static void wfx_mcast_stop_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_stop_work); + + if (wvif->aid0_bit_set) { + del_timer_sync(&wvif->mcast_timeout); + wfx_tx_lock_flush(wvif->wdev); + wvif->aid0_bit_set = false; + wfx_set_tim_impl(wvif, false); + wfx_tx_unlock(wvif->wdev); + } +} + +#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) +static void wfx_mcast_timeout(unsigned long arg) +{ + struct wfx_vif *wvif = (struct wfx_vif *)arg; +#else +static void wfx_mcast_timeout(struct timer_list *t) +{ + struct wfx_vif *wvif = from_timer(wvif, t, mcast_timeout); +#endif + dev_warn(wvif->wdev->dev, "multicast delivery timeout\n"); + spin_lock_bh(&wvif->ps_state_lock); + wvif->mcast_tx = wvif->aid0_bit_set && wvif->mcast_buffered; + if (wvif->mcast_tx) + wfx_bh_request_tx(wvif->wdev); + spin_unlock_bh(&wvif->ps_state_lock); +} + int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int i; struct wfx_dev *wdev = hw->priv; struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + // FIXME: parameters are set by kernel juste after interface_add. + // Keep struct hif_req_edca_queue_params blank? + struct hif_req_edca_queue_params default_edca_params[] = { + [IEEE80211_AC_VO] = { + .queue_id = HIF_QUEUE_ID_VOICE, + .aifsn = 2, + .cw_min = 3, + .cw_max = 7, + .tx_op_limit = TXOP_UNIT * 47, + }, + [IEEE80211_AC_VI] = { + .queue_id = HIF_QUEUE_ID_VIDEO, + .aifsn = 2, + .cw_min = 7, + .cw_max = 15, + .tx_op_limit = TXOP_UNIT * 94, + }, + [IEEE80211_AC_BE] = { + .queue_id = HIF_QUEUE_ID_BESTEFFORT, + .aifsn = 3, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + [IEEE80211_AC_BK] = { + .queue_id = HIF_QUEUE_ID_BACKGROUND, + .aifsn = 7, + .cw_min = 15, + .cw_max = 1023, + .tx_op_limit = TXOP_UNIT * 0, + }, + }; + + if (wfx_api_older_than(wdev, 2, 0)) { + default_edca_params[IEEE80211_AC_BE].queue_id = HIF_QUEUE_ID_BACKGROUND; + default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT; + } for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { if (!wdev->vif[i]) { @@ -28,12 +146,33 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) wvif->vif = vif; wvif->wdev = wdev; + INIT_WORK(&wvif->link_id_work, wfx_link_id_work); + INIT_DELAYED_WORK(&wvif->link_id_gc_work, wfx_link_id_gc_work); + + spin_lock_init(&wvif->ps_state_lock); + + INIT_WORK(&wvif->mcast_start_work, wfx_mcast_start_work); + INIT_WORK(&wvif->mcast_stop_work, wfx_mcast_stop_work); +#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) + setup_timer(&wvif->mcast_timeout, wfx_mcast_timeout, (unsigned long) wvif); +#else + timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); +#endif + BUG_ON(ARRAY_SIZE(default_edca_params) != ARRAY_SIZE(wvif->edca.params)); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + memcpy(&wvif->edca.params[i], &default_edca_params[i], sizeof(default_edca_params[i])); + tx_policy_init(wvif); return 0; } void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + + wfx_tx_queues_wait_empty_vif(wvif); + cancel_delayed_work_sync(&wvif->link_id_gc_work); + del_timer_sync(&wvif->mcast_timeout); } int wfx_start(struct ieee80211_hw *hw) @@ -43,4 +182,10 @@ int wfx_start(struct ieee80211_hw *hw) void wfx_stop(struct ieee80211_hw *hw) { + struct wfx_dev *wdev = hw->priv; + + wfx_tx_lock_flush(wdev); + wfx_tx_queues_clear(wdev); + wfx_tx_unlock(wdev); + WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked"); } diff --git a/drivers/staging/wfx/sta.h b/drivers/staging/wfx/sta.h index f17b4d1511d7..f36d94f907c7 100644 --- a/drivers/staging/wfx/sta.h +++ b/drivers/staging/wfx/sta.h @@ -10,6 +10,14 @@ #include +#include "hif_api_cmd.h" + +struct wfx_edca_params { + /* NOTE: index is a linux queue id. */ + struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS]; + bool uapsd_enable[IEEE80211_NUM_ACS]; +}; + struct wfx_sta_priv { int link_id; int vif_id; diff --git a/drivers/staging/wfx/traces.h b/drivers/staging/wfx/traces.h index fd75c4c7e9c7..d3e27050f84a 100644 --- a/drivers/staging/wfx/traces.h +++ b/drivers/staging/wfx/traces.h @@ -13,6 +13,7 @@ #include #include +#include #include "bus.h" #include "hif_api_cmd.h" @@ -354,6 +355,79 @@ TRACE_EVENT(bh_stats, ); #define _trace_bh_stats(ind, req, cnf, busy, release) trace_bh_stats(ind, req, cnf, busy, release) +TRACE_EVENT(tx_stats, + TP_PROTO(struct hif_cnf_tx *tx_cnf, struct sk_buff *skb, int delay), + TP_ARGS(tx_cnf, skb, delay), + TP_STRUCT__entry( + __field(int, pkt_id) + __field(int, delay_media) + __field(int, delay_queue) + __field(int, delay_fw) + __field(int, ack_failures) + __field(int, flags) + __array(int, rate, 4) + __array(int, tx_count, 4) + ), + TP_fast_assign( + // Keep sync with wfx_rates definition in main.c + static const int hw_rate[] = { 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13 }; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rates = tx_info->driver_rates; + int i; + + __entry->pkt_id = tx_cnf->packet_id; + __entry->delay_media = tx_cnf->media_delay; + __entry->delay_queue = tx_cnf->tx_queue_delay; + __entry->delay_fw = delay; + __entry->ack_failures = tx_cnf->ack_failures; + if (!tx_cnf->status || __entry->ack_failures) + __entry->ack_failures += 1; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + if (rates[0].flags & IEEE80211_TX_RC_MCS) + __entry->rate[i] = rates[i].idx; + else + __entry->rate[i] = hw_rate[rates[i].idx]; + __entry->tx_count[i] = rates[i].count; + } + __entry->flags = 0; + if (rates[0].flags & IEEE80211_TX_RC_MCS) + __entry->flags |= 0x01; + if (rates[0].flags & IEEE80211_TX_RC_SHORT_GI) + __entry->flags |= 0x02; + if (rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD) + __entry->flags |= 0x04; + if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + __entry->flags |= 0x08; + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) + __entry->flags |= 0x10; + if (tx_cnf->status) + __entry->flags |= 0x20; + if (tx_cnf->status == HIF_REQUEUE) + __entry->flags |= 0x40; + ), + TP_printk("packet ID: %08x, rate policy: %s %d|%d %d|%d %d|%d %d|%d -> %d attempt, Delays media/queue/total: %4dus/%4dus/%4dus", + __entry->pkt_id, + __print_flags(__entry->flags, NULL, + { 0x01, "M" }, { 0x02, "S" }, { 0x04, "G" }, + { 0x08, "R" }, { 0x10, "D" }, { 0x20, "F" }, + { 0x40, "Q" }), + __entry->rate[0], + __entry->tx_count[0], + __entry->rate[1], + __entry->tx_count[1], + __entry->rate[2], + __entry->tx_count[2], + __entry->rate[3], + __entry->tx_count[3], + __entry->ack_failures, + __entry->delay_media, + __entry->delay_queue, + __entry->delay_fw + ) +); +#define _trace_tx_stats(tx_cnf, skb, delay) trace_tx_stats(tx_cnf, skb, delay) + #endif /* This part must be outside protection */ diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 48071e1c989c..322e46e343d5 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -15,11 +15,29 @@ #include #include "bh.h" +#include "data_tx.h" #include "main.h" +#include "queue.h" #include "secure_link.h" +#include "sta.h" #include "hif_tx.h" #include "hif_api_general.h" +#if (KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE) +#define nl80211_band ieee80211_band +#define NL80211_BAND_2GHZ IEEE80211_BAND_2GHZ +#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS +#endif + +#if (KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE) +static inline u8 ieee80211_get_tid(struct ieee80211_hdr *hdr) +{ + u8 *qc = ieee80211_get_qos_ctl(hdr); + + return qc[0] & IEEE80211_QOS_CTL_TID_MASK; +} +#endif + #if (KERNEL_VERSION(4, 17, 0) > LINUX_VERSION_CODE) #define struct_size(p, member, n) \ (n * sizeof(*(p)->member) + __must_be_array((p)->member) + sizeof(*(p))) @@ -44,6 +62,10 @@ struct wfx_dev { int chip_frozen; struct wfx_hif_cmd hif_cmd; + struct wfx_queue tx_queue[4]; + struct wfx_queue_stats tx_queue_stats; + int tx_burst_idx; + atomic_t tx_lock; struct hif_rx_stats rx_stats; struct mutex rx_stats_lock; @@ -53,6 +75,28 @@ struct wfx_vif { struct wfx_dev *wdev; struct ieee80211_vif *vif; int id; + + + u32 link_id_map; + struct wfx_link_entry link_id_db[WFX_MAX_STA_IN_AP_MODE]; + struct delayed_work link_id_gc_work; + struct work_struct link_id_work; + + bool aid0_bit_set; + bool mcast_tx; + bool mcast_buffered; + struct timer_list mcast_timeout; + struct work_struct mcast_start_work; + struct work_struct mcast_stop_work; + + + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + spinlock_t ps_state_lock; + + struct wfx_edca_params edca; }; static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) @@ -68,4 +112,33 @@ static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv; } +static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev, struct wfx_vif *cur) +{ + int i; + int mark = 0; + struct wfx_vif *tmp; + + if (!cur) + mark = 1; + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + tmp = wdev_to_wvif(wdev, i); + if (mark && tmp) + return tmp; + if (tmp == cur) + mark = 1; + } + return NULL; +} + +static inline int memzcmp(void *src, unsigned int size) +{ + uint8_t *buf = src; + + if (!size) + return 0; + if (*buf) + return 1; + return memcmp(buf, buf + 1, size - 1); +} + #endif /* WFX_H */ From patchwork Thu Sep 19 10:52:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152161 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5852A14DB for ; Thu, 19 Sep 2019 10:54:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 239C521924 for ; Thu, 19 Sep 2019 10:54:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="LbQkfUQJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389505AbfISKwx (ORCPT ); Thu, 19 Sep 2019 06:52:53 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389448AbfISKwv (ORCPT ); Thu, 19 Sep 2019 06:52:51 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=RwcHcCi9gRaDP4dEEZEM6PhQcVi7l75fXOTzIVrvvKyURxUaNBgPd5Y1NBjORmGChO7iUeN4tS9PZRX+jRo+TJNiB8ffwHNovOQFVhNHzp3QplPJ1iw6pKtSwx0ZUrl/AzDFS+F53R9JESFQm++V3oBWDGInvahwX2w+KzVBlEGkZcFaQsAxVw9Y+qgPa6zvhoYordiK5PVqduHeAZg7k/N8JiQ/TcqP/e9YZaFFwJHruDLtawcrY7++FcEmHcNcYZmkR7hSCF9o9tmXxD+kJtH33bq1BB8dTEs1LFUbszScRz6fF4lJ7kLDVDhf/c7TMt8RI3dTEwo8DAHf+SkCnQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WiXNtcEm4a7neHAFun+tjvgyc+uhFsoW1O6h6kW7pCg=; b=DO3pgteqA27SU3iJOeSvGsuyD2cOQv/zQseq1/CNm1lf5fq9Qu5MYoFT/GzJGT6HepRFyaNaNkHkZHeJMy8IduLq1AykAywkDxknT7CjbMm+uitrWb1eCg29j0YSccV/miQRicBjaS+4W9A8DvnwbmhbrzvP6sM+s6bWKVp6lJTo6AzRQlk3Jz2l0OtkSYl5V29Buve5Au7U7MtCofs3drh+Qdr+8crd5sQ62Ag505/i+O1BYrrHj1Ix0KRSt5znljM2HX7C1HjmY+Y1i+QVQ0J+1Cs3ZhH12RfIRE2Co9CR8RJr0EPmk2Ra5PIRnmY3wEzn8vnadnJWsQdAABgoGA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WiXNtcEm4a7neHAFun+tjvgyc+uhFsoW1O6h6kW7pCg=; b=LbQkfUQJaqkgVY9ZI/KcySWuDtpv1lbEkhQB5hBcCQ50rRgNcSQsIHE6v7HXd7wU1uRcTIivFKSKvAWv1F5jmP1+TpQy5LSYdLOKS8p+lCsgbRN23at0cNzmdilw4dhpzQYKlRqR5TWUrmOlDeWRdIPb8Y/1OUDRjegeopbPJwY= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:44 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:44 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 17/20] staging: wfx: allow to receive 802.11 frames Thread-Topic: [PATCH 17/20] staging: wfx: allow to receive 802.11 frames Thread-Index: AQHVbthdWFf+fTi6tkKyaLIBTbBBtQ== Date: Thu, 19 Sep 2019 10:52:43 +0000 Message-ID: <20190919105153.15285-18-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8587b316-07e9-45e3-cc9a-08d73cef808a x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:608; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(43544003)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: XKSZ8HpH13821WVLTcUv35BP2IoRholX17ifQOWP3dtw8IPj32ciUcdyG2aYTSstRkUE6EaGv/GH4522xnMxngLJY2CEL/I/A9cUe/Jkzi3oK99yoJLYadBI9ZTWxhCwXoIGuOQwJOJkRok4wQ18Dz24VlTiBqePYrL945mBW62sTBJN1J65cLTuUOCSCf+uE2SDbCqjX/N9zyFmQULSKqwKHMietBvP0AKoYEOZC5NmaFDnoRMiBpAM7H7aiSzTFBBl3Rwlo/cn2vY+Gn/IndmYwdSUeNQiLm8jqh4iinhrnRgrvVKM4GdXK3olLjLzg1y60gt3+YybAeJFxH984saFSwwkIlM2dMcNUq1jMa76JsiaZj0wngxMPTaDXo9GLiSok8l45YtKJxvNwFXKtUmSxUDolmSFu7dskcltalM= Content-ID: <98397164D0903C41B2441EDFE2D155D0@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8587b316-07e9-45e3-cc9a-08d73cef808a X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:43.2963 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 5mYTu3fVtg+vAmzIzIqU/h5xW8zf7j9IkiGfbwA8dLeGFkvCj9hbexauv/Km1iOQ49jqcBPr8lWxTXJjo6hlYA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Again, this task is more complex than it should since driver try to handle itself power saving of stations. Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/data_rx.c | 187 ++++++++++++++++++++++++++++++++++ drivers/staging/wfx/data_rx.h | 18 ++++ drivers/staging/wfx/hif_rx.c | 23 +++++ 4 files changed, 229 insertions(+) create mode 100644 drivers/staging/wfx/data_rx.c create mode 100644 drivers/staging/wfx/data_rx.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index d5ac9fafd1f1..d9e21515d08e 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -11,6 +11,7 @@ wfx-y := \ hif_rx.o \ queue.o \ data_tx.o \ + data_rx.o \ sta.o \ main.o \ sta.o \ diff --git a/drivers/staging/wfx/data_rx.c b/drivers/staging/wfx/data_rx.c new file mode 100644 index 000000000000..6544d00d1657 --- /dev/null +++ b/drivers/staging/wfx/data_rx.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include +#include + +#include "data_rx.h" +#include "wfx.h" +#include "bh.h" +#include "sta.h" + +static int wfx_handle_pspoll(struct wfx_vif *wvif, struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int i; + + if (!ether_addr_equal(wvif->vif->addr, pspoll->bssid)) + return 1; + + rcu_read_lock(); + sta = ieee80211_find_sta(wvif->vif, pspoll->ta); + if (sta) + link_id = ((struct wfx_sta_priv *) &sta->drv_priv)->link_id; + rcu_read_unlock(); + if (link_id) + pspoll_mask = BIT(link_id); + else + return 1; + + wvif->pspoll_mask |= pspoll_mask; + /* Do not report pspols if data for given link id is queued already. */ + for (i = 0; i < IEEE80211_NUM_ACS; ++i) { + if (wfx_tx_queue_get_num_queued(&wvif->wdev->tx_queue[i], + pspoll_mask)) { + wfx_bh_request_tx(wvif->wdev); + return 1; + } + } + return 0; +} + +static int wfx_drop_encrypt_data(struct wfx_dev *wdev, struct hif_ind_rx *arg, struct sk_buff *skb) +{ + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) skb->data; + size_t hdrlen = ieee80211_hdrlen(frame->frame_control); + size_t iv_len, icv_len; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed. + */ + switch (arg->rx_flags.encryp) { + case HIF_RI_FLAGS_WEP_ENCRYPTED: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case HIF_RI_FLAGS_TKIP_ENCRYPTED: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + break; + case HIF_RI_FLAGS_AES_ENCRYPTED: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case HIF_RI_FLAGS_WAPI_ENCRYPTED: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + dev_err(wdev->dev, "unknown encryption type %d\n", + arg->rx_flags.encryp); + return -EIO; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == HIF_STATUS_MICFAILURE) + icv_len = 0; + + if (skb->len < hdrlen + iv_len + icv_len) { + dev_warn(wdev->dev, "malformed SDU received\n"); + return -EIO; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + return 0; + +} + +void wfx_rx_cb(struct wfx_vif *wvif, struct hif_ind_rx *arg, struct sk_buff *skb) +{ + int link_id = arg->rx_flags.peer_sta_id; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data; + struct wfx_link_entry *entry = NULL; + bool early_data = false; + + memset(hdr, 0, sizeof(*hdr)); + + // FIXME: Why do we drop these frames? + if (!arg->rcpi_rssi && + (ieee80211_is_probe_resp(frame->frame_control) || + ieee80211_is_beacon(frame->frame_control))) + goto drop; + + if (link_id && link_id <= WFX_MAX_STA_IN_AP_MODE) { + entry = &wvif->link_id_db[link_id - 1]; + entry->timestamp = jiffies; + if (entry->status == WFX_LINK_SOFT && ieee80211_is_data(frame->frame_control)) + early_data = true; + } + + if (arg->status == HIF_STATUS_MICFAILURE) + hdr->flag |= RX_FLAG_MMIC_ERROR; + else if (arg->status) + goto drop; + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + dev_warn(wvif->wdev->dev, "malformed SDU received\n"); + goto drop; + } + + if (ieee80211_is_pspoll(frame->frame_control)) + if (wfx_handle_pspoll(wvif, skb)) + goto drop; + + hdr->band = NL80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency(arg->channel_number, hdr->band); + + if (arg->rxed_rate >= 14) { +#if (KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE) + hdr->flag |= RX_FLAG_HT; +#else + hdr->encoding = RX_ENC_HT; +#endif + hdr->rate_idx = arg->rxed_rate - 14; + } else if (arg->rxed_rate >= 4) { + hdr->rate_idx = arg->rxed_rate - 2; + } else { + hdr->rate_idx = arg->rxed_rate; + } + + hdr->signal = arg->rcpi_rssi / 2 - 110; + hdr->antenna = 0; + + if (arg->rx_flags.encryp) { + if (wfx_drop_encrypt_data(wvif->wdev, arg, skb)) + goto drop; + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + if (arg->rx_flags.encryp == HIF_RI_FLAGS_TKIP_ENCRYPTED) + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + } + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (ieee80211_is_action(frame->frame_control) + && arg->rx_flags.match_uc_addr + && mgmt->u.action.category == WLAN_CATEGORY_BACK) + goto drop; + + if (early_data) { + spin_lock_bh(&wvif->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == WFX_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(wvif->wdev->hw, skb); + spin_unlock_bh(&wvif->ps_state_lock); + } else { + ieee80211_rx_irqsafe(wvif->wdev->hw, skb); + } + + return; + +drop: + dev_kfree_skb(skb); +} diff --git a/drivers/staging/wfx/data_rx.h b/drivers/staging/wfx/data_rx.h new file mode 100644 index 000000000000..b44d15268f83 --- /dev/null +++ b/drivers/staging/wfx/data_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Datapath implementation. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_DATA_RX_H +#define WFX_DATA_RX_H + +#include "hif_api_cmd.h" + +struct wfx_vif; +struct sk_buff; + +void wfx_rx_cb(struct wfx_vif *wvif, struct hif_ind_rx *arg, struct sk_buff *skb); + +#endif /* WFX_DATA_RX_H */ diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index 97c4c2f082fb..c07984b0535d 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -11,6 +11,7 @@ #include "hif_rx.h" #include "wfx.h" +#include "data_rx.h" #include "secure_link.h" #include "hif_api_cmd.h" @@ -127,6 +128,21 @@ static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void * return 0; } +static int hif_receive_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf, struct sk_buff *skb) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_rx *body = buf; + + if (!wvif) { + dev_warn(wdev->dev, "ignore rx data for non existant vif %d\n", hif->interface); + return 0; + } + skb_pull(skb, sizeof(struct hif_msg) + sizeof(struct hif_ind_rx)); + wfx_rx_cb(wvif, body, skb); + + return 0; +} + static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); @@ -218,6 +234,8 @@ static const struct { { HIF_IND_ID_GENERIC, hif_generic_indication }, { HIF_IND_ID_ERROR, hif_error_indication }, { HIF_IND_ID_EXCEPTION, hif_exception_indication }, + // FIXME: allocate skb_p from hif_receive_indication and make it generic + //{ HIF_IND_ID_RX, hif_receive_indication }, }; void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) @@ -226,6 +244,11 @@ void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb) struct hif_msg *hif = (struct hif_msg *) skb->data; int hif_id = hif->id; + if (hif_id == HIF_IND_ID_RX) { + // hif_receive_indication take care of skb lifetime + hif_receive_indication(wdev, hif, hif->body, skb); + return; + } // Note: mutex_is_lock cause an implicit memory barrier that protect // buf_send if (mutex_is_locked(&wdev->hif_cmd.lock) From patchwork Thu Sep 19 10:52:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152151 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 78FD514DB for ; Thu, 19 Sep 2019 10:54:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1D27C2196F for ; Thu, 19 Sep 2019 10:54:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="TZWbFpWJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730815AbfISKxt (ORCPT ); Thu, 19 Sep 2019 06:53:49 -0400 Received: from mail-eopbgr700049.outbound.protection.outlook.com ([40.107.70.49]:8577 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389558AbfISKxC (ORCPT ); Thu, 19 Sep 2019 06:53:02 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=HoI/M7CYA39SjjnPRN3QxmkGTpH2/a7dDGnBDmTZX4Jho44jxAXbyTSOWfoWzKENcdPvGn8cfznYhlxOH4s2eIpxkDf7FDlPLCAcfzTihSFIqx0W9Vac8H58Wl1TLBRSQkrSH9FA9zHm8YngxDvIUWOPa/b669WfJ1v5x11PQRyhvERFQy/xmY84aPk5AqRqOeOGNfAxYIcIrBk4TEUbWUbwhT1dhocFL4mxBJIXvt3bkvjXfUoNcRea8X56lMtsrL9jrNdV8LG9IPCDBm7tQMXEhUraCAi5V/nM3fL2t81W1HNbMfOBohRbpaLidsLwAgnefBbv23mblyp7ofw0eA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=eMDXEIK0GajkaZmsT0bvqXY3S2T7rgTWgngOjQkXfgs=; b=MKj8iqFdV9PXDxUbqnddHKgQ88o11Dpzi72GMbbyUVbZ2w8Wg6aJqk1HOqnGbSnXaijlUGImjHLnXugYUY3Ue0nu1GWOCp6eQBEVJIKEEX/4wMG/4T8z+fuatcUJMa9X+624YqrZSpg3q88etCP2lpHkKYzU62krWOXKh2+3KMCWx0UNEosvx/Y1uITH5luXdaHjZJZuI0w+/R234/z9NAZ4JyRRYpsmuYL9iN5j2zlUtcV0PMrJaOT/5Xlr6K7r63qvMNayZ9mt/rXv4MLIfUXpIdZZOlfsGbSPaWtIcPuo4lJfU25PUEAKeMuN/LblIrRZwoKmvmUClgSoUHQ9wA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=eMDXEIK0GajkaZmsT0bvqXY3S2T7rgTWgngOjQkXfgs=; b=TZWbFpWJVUNRf8gdMQ9EXKwCLzwz/kXGVpKeVPWTEBcBx7wo2rdTsUiZvhAo4TzytSZT7Wf1hvjN598lOe+RcPMewx8vRV8c7U59I8660GQpMkeBsYvfJoCZGkwMlbbbQrH6r2Muz4oVVjl3/yP1f9vy6A8eW/XpltV+9DRq+Fs= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB3775.namprd11.prod.outlook.com (20.178.253.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:45 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:45 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 18/20] staging: wfx: allow to scan networks Thread-Topic: [PATCH 18/20] staging: wfx: allow to scan networks Thread-Index: AQHVbthdJ65Ee2NEtU+XaKoJWLw7Jg== Date: Thu, 19 Sep 2019 10:52:43 +0000 Message-ID: <20190919105153.15285-19-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 8af426fe-f0a0-4efe-228d-08d73cef80d6 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB3775; x-ms-traffictypediagnostic: MN2PR11MB3775: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:127; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(366004)(376002)(346002)(39850400004)(136003)(396003)(199004)(189003)(86362001)(256004)(14444005)(8936002)(4326008)(66066001)(36756003)(14454004)(305945005)(64756008)(5660300002)(66476007)(66556008)(66446008)(316002)(476003)(446003)(11346002)(110136005)(2616005)(66946007)(478600001)(91956017)(25786009)(76116006)(7736002)(71190400001)(81166006)(1076003)(30864003)(2501003)(6506007)(186003)(6436002)(486006)(81156014)(3846002)(6116002)(8676002)(102836004)(107886003)(54906003)(76176011)(2906002)(26005)(66574012)(99286004)(6486002)(71200400001)(6512007);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB3775;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: djBpp9zFB2jT/2fPt8+noYc9k1qCNbH6iDa9qdAZ/+vWkJsBM0Bw2akd77tH1RKvECkD/Z383c2Gw3R8P3pO69ICWLFTbeOHkCtOJ69uYeduepz78KHltCwsCoJV+md/HO7Fp/wBoQCas6VE8UcoNuuowsiRwFXSHyTfj4gDtLrlgV9W30uAqr+/WzgeqJo/ZEIpFC0rPlcs25cA0yfYzU9JVi44bgommi4dd+lrL4l07V7wh42pzv8Iw4PwMYvFlKnNxTf4AcpmHqreJXBmcq4T/PkkZnkHP5TlPgeZj5QynW7lbKISvE3UFz2MdoQVtXrpDidxQeax5XhlITDoyvNO09Rkq74IHOGGsONUqJg4wcCQukD0ls4AVAhRdxLBwsZalhavfa7ilVFnjBgds59Mj7ZTusld+IQAMlqSqFA= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8af426fe-f0a0-4efe-228d-08d73cef80d6 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:43.7840 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: K0PPiOyXWWdoIbDGJlLWdG16Y42NYz6H7Iq4GZKL3AFu2bw81RLIJmhPqDAVYJ+I+OHYnVM7VsWAceJWPqhcRw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB3775 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/bh.c | 2 +- drivers/staging/wfx/hif_rx.c | 13 ++ drivers/staging/wfx/main.c | 5 + drivers/staging/wfx/scan.c | 258 +++++++++++++++++++++++++++++++++++ drivers/staging/wfx/scan.h | 42 ++++++ drivers/staging/wfx/sta.c | 23 +++- drivers/staging/wfx/sta.h | 4 + drivers/staging/wfx/wfx.h | 11 ++ 9 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/wfx/scan.c create mode 100644 drivers/staging/wfx/scan.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index d9e21515d08e..2b8a5fa86fac 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -12,6 +12,7 @@ wfx-y := \ queue.o \ data_tx.o \ data_rx.o \ + scan.o \ sta.o \ main.o \ sta.o \ diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index ed81c3924d98..6000c03bb658 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -268,7 +268,7 @@ static void bh_work(struct work_struct *work) if (last_op_is_rx) ack_sdio_data(wdev); - if (!wdev->hif.tx_buffers_used && !work_pending(work)) { + if (!wdev->hif.tx_buffers_used && !work_pending(work) && !atomic_read(&wdev->scan_in_progress)) { device_release(wdev); release_chip = true; } diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index c07984b0535d..d386fab0a90f 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -11,6 +11,7 @@ #include "hif_rx.h" #include "wfx.h" +#include "scan.h" #include "data_rx.h" #include "secure_link.h" #include "hif_api_cmd.h" @@ -143,6 +144,17 @@ static int hif_receive_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi return 0; } +static int hif_scan_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_scan_cmpl *body = buf; + + WARN_ON(!wvif); + wfx_scan_complete_cb(wvif, body); + + return 0; +} + static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); @@ -230,6 +242,7 @@ static const struct { { HIF_IND_ID_STARTUP, hif_startup_indication }, { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication }, + { HIF_IND_ID_SCAN_CMPL, hif_scan_complete_indication }, { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, { HIF_IND_ID_GENERIC, hif_generic_indication }, { HIF_IND_ID_ERROR, hif_error_indication }, diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index cce4e30dd94a..06220bac5b75 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -55,6 +55,7 @@ static const struct ieee80211_ops wfx_ops = { .add_interface = wfx_add_interface, .remove_interface = wfx_remove_interface, .tx = wfx_tx, + .hw_scan = wfx_hw_scan, }; bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) @@ -203,6 +204,8 @@ struct wfx_dev *wfx_init_common(struct device *dev, hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg) + sizeof(struct hif_req_tx) + 4 /* alignment */ + 8 /* TKIP IV */; + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; wdev = hw->priv; wdev->hw = hw; @@ -214,6 +217,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup"); wfx_fill_sl_key(dev, &wdev->pdata); + mutex_init(&wdev->conf_mutex); mutex_init(&wdev->rx_stats_lock); init_completion(&wdev->firmware_ready); wfx_init_hif_cmd(&wdev->hif_cmd); @@ -225,6 +229,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, void wfx_free_common(struct wfx_dev *wdev) { mutex_destroy(&wdev->rx_stats_lock); + mutex_destroy(&wdev->conf_mutex); wfx_tx_queues_deinit(wdev); ieee80211_free_hw(wdev->hw); } diff --git a/drivers/staging/wfx/scan.c b/drivers/staging/wfx/scan.c new file mode 100644 index 000000000000..89af294cf23d --- /dev/null +++ b/drivers/staging/wfx/scan.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Scan related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "scan.h" +#include "wfx.h" +#include "sta.h" +#include "hif_tx_mib.h" + +static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool aborted) +{ +#if (KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE) + ieee80211_scan_completed(hw, aborted); +#else + struct cfg80211_scan_info info = { + .aborted = aborted ? 1 : 0, + }; + + ieee80211_scan_completed(hw, &info); +#endif +} + +static int wfx_scan_start(struct wfx_vif *wvif, struct wfx_scan_params *scan) +{ + int ret; + int tmo = 500; + + tmo += scan->scan_req.num_of_channels * + ((20 * (scan->scan_req.max_channel_time)) + 10); + atomic_set(&wvif->scan.in_progress, 1); + atomic_set(&wvif->wdev->scan_in_progress, 1); + + schedule_delayed_work(&wvif->scan.timeout, msecs_to_jiffies(tmo)); + ret = hif_scan(wvif, scan); + if (ret) { + wfx_scan_failed_cb(wvif); + atomic_set(&wvif->scan.in_progress, 0); + atomic_set(&wvif->wdev->scan_in_progress, 0); + cancel_delayed_work_sync(&wvif->scan.timeout); + } + return ret; +} + +int wfx_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct cfg80211_scan_request *req = &hw_req->req; + struct sk_buff *skb; + int i, ret; + struct hif_mib_template_frame *p; + + if (!wvif) + return -EINVAL; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + if (req->n_ssids > HIF_API_MAX_NB_SSIDS) + return -EINVAL; + +#if (KERNEL_VERSION(3, 19, 0) > LINUX_VERSION_CODE) + skb = ieee80211_probereq_get(hw, wvif->vif, NULL, 0, req->ie_len); +#else + skb = ieee80211_probereq_get(hw, wvif->vif->addr, NULL, 0, req->ie_len); +#endif + if (!skb) + return -ENOMEM; + + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); + + mutex_lock(&wdev->conf_mutex); + + p = (struct hif_mib_template_frame *)skb_push(skb, 4); + p->frame_type = HIF_TMPLT_PRBREQ; + p->frame_length = cpu_to_le16(skb->len - 4); + ret = hif_set_template_frame(wvif, p); + skb_pull(skb, 4); + + if (!ret) + /* Host want to be the probe responder. */ + ret = wfx_fwd_probe_req(wvif, true); + if (ret) { + mutex_unlock(&wdev->conf_mutex); + dev_kfree_skb(skb); + return ret; + } + + wfx_tx_lock_flush(wdev); + + BUG_ON(wvif->scan.req); + wvif->scan.req = req; + wvif->scan.n_ssids = 0; + wvif->scan.status = 0; + wvif->scan.begin = &req->channels[0]; + wvif->scan.curr = wvif->scan.begin; + wvif->scan.end = &req->channels[req->n_channels]; + wvif->scan.output_power = wdev->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct hif_ssid_def *dst = &wvif->scan.ssids[wvif->scan.n_ssids]; + + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->ssid_length = req->ssids[i].ssid_len; + ++wvif->scan.n_ssids; + } + + mutex_unlock(&wdev->conf_mutex); + + if (skb) + dev_kfree_skb(skb); + schedule_work(&wvif->scan.work); + return 0; +} + +void wfx_scan_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.work); + struct ieee80211_channel **it; + struct wfx_scan_params scan = { + .scan_req.scan_type.type = 0, /* Foreground */ + }; + struct ieee80211_channel *first; + int i; + + down(&wvif->scan.lock); + mutex_lock(&wvif->wdev->conf_mutex); + + if (!wvif->scan.req || wvif->scan.curr == wvif->scan.end) { + if (wvif->scan.output_power != wvif->wdev->output_power) + hif_set_output_power(wvif, wvif->wdev->output_power * 10); + + if (wvif->scan.status < 0) + dev_warn(wvif->wdev->dev, "scan failed\n"); + else if (wvif->scan.req) + dev_dbg(wvif->wdev->dev, "scan completed\n"); + else + dev_dbg(wvif->wdev->dev, "scan canceled\n"); + + wvif->scan.req = NULL; + wfx_tx_unlock(wvif->wdev); + mutex_unlock(&wvif->wdev->conf_mutex); + __ieee80211_scan_completed_compat(wvif->wdev->hw, wvif->scan.status ? 1 : 0); + up(&wvif->scan.lock); + return; + } + first = *wvif->scan.curr; + + for (it = wvif->scan.curr + 1, i = 1; + it != wvif->scan.end && i < HIF_API_MAX_NB_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_NO_IR) + break; + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + (*it)->max_power != first->max_power) + break; + } + scan.scan_req.band = first->band; + + if (wvif->scan.req->no_cck) + scan.scan_req.max_transmit_rate = API_RATE_INDEX_G_6MBPS; + else + scan.scan_req.max_transmit_rate = API_RATE_INDEX_B_1MBPS; + scan.scan_req.num_of_probe_requests = + (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; + scan.scan_req.num_of_ssi_ds = wvif->scan.n_ssids; + scan.ssids = &wvif->scan.ssids[0]; + scan.scan_req.num_of_channels = it - wvif->scan.curr; + scan.scan_req.probe_delay = 100; + + scan.ch = kcalloc(scan.scan_req.num_of_channels, sizeof(u8), GFP_KERNEL); + + if (!scan.ch) { + wvif->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.scan_req.num_of_channels; ++i) + scan.ch[i] = wvif->scan.curr[i]->hw_value; + + if (wvif->scan.curr[0]->flags & IEEE80211_CHAN_NO_IR) { + scan.scan_req.min_channel_time = 50; + scan.scan_req.max_channel_time = 150; + } else { + scan.scan_req.min_channel_time = 10; + scan.scan_req.max_channel_time = 50; + } + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + wvif->scan.output_power != first->max_power) { + wvif->scan.output_power = first->max_power; + hif_set_output_power(wvif, wvif->scan.output_power * 10); + } + wvif->scan.status = wfx_scan_start(wvif, &scan); + kfree(scan.ch); + if (wvif->scan.status) + goto fail; + wvif->scan.curr = it; + mutex_unlock(&wvif->wdev->conf_mutex); + return; + +fail: + wvif->scan.curr = wvif->scan.end; + mutex_unlock(&wvif->wdev->conf_mutex); + up(&wvif->scan.lock); + schedule_work(&wvif->scan.work); +} + +static void wfx_scan_complete(struct wfx_vif *wvif) +{ + up(&wvif->scan.lock); + atomic_set(&wvif->wdev->scan_in_progress, 0); + + wfx_scan_work(&wvif->scan.work); +} + +void wfx_scan_failed_cb(struct wfx_vif *wvif) +{ + if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) { + wvif->scan.status = -EIO; + schedule_work(&wvif->scan.timeout.work); + } +} + +void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg) +{ + if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) { + wvif->scan.status = 1; + schedule_work(&wvif->scan.timeout.work); + } +} + +void wfx_scan_timeout(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.timeout.work); + + if (atomic_xchg(&wvif->scan.in_progress, 0)) { + if (wvif->scan.status > 0) { + wvif->scan.status = 0; + } else if (!wvif->scan.status) { + dev_warn(wvif->wdev->dev, "timeout waiting for scan complete notification\n"); + wvif->scan.status = -ETIMEDOUT; + wvif->scan.curr = wvif->scan.end; + hif_stop_scan(wvif); + } + wfx_scan_complete(wvif); + } +} diff --git a/drivers/staging/wfx/scan.h b/drivers/staging/wfx/scan.h new file mode 100644 index 000000000000..b4ddd0771a9b --- /dev/null +++ b/drivers/staging/wfx/scan.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Scan related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_SCAN_H +#define WFX_SCAN_H + +#include +#include +#include + +#include "hif_api_cmd.h" + +struct wfx_dev; +struct wfx_vif; + +struct wfx_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct hif_ssid_def ssids[HIF_API_MAX_NB_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; +}; + +int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req); +void wfx_scan_work(struct work_struct *work); +void wfx_scan_timeout(struct work_struct *work); +void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg); +void wfx_scan_failed_cb(struct wfx_vif *wvif); + +#endif /* WFX_SCAN_H */ diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index 1b45be530070..c9a35a5307dd 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -10,9 +10,18 @@ #include "sta.h" #include "wfx.h" +#include "scan.h" +#include "hif_tx_mib.h" #define TXOP_UNIT 32 +int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable) +{ + wvif->fwd_probe_req = enable; + return hif_set_rx_filter(wvif, wvif->filter_bssid, + wvif->fwd_probe_req); +} + static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) { struct sk_buff *skb; @@ -134,6 +143,8 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT; } + mutex_lock(&wdev->conf_mutex); + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { if (!wdev->vif[i]) { wdev->vif[i] = vif; @@ -141,8 +152,10 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) break; } } - if (i == ARRAY_SIZE(wdev->vif)) + if (i == ARRAY_SIZE(wdev->vif)) { + mutex_unlock(&wdev->conf_mutex); return -EOPNOTSUPP; + } wvif->vif = vif; wvif->wdev = wdev; @@ -158,6 +171,12 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) #else timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); #endif + + sema_init(&wvif->scan.lock, 1); + INIT_WORK(&wvif->scan.work, wfx_scan_work); + INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout); + + mutex_unlock(&wdev->conf_mutex); BUG_ON(ARRAY_SIZE(default_edca_params) != ARRAY_SIZE(wvif->edca.params)); for (i = 0; i < IEEE80211_NUM_ACS; i++) memcpy(&wvif->edca.params[i], &default_edca_params[i], sizeof(default_edca_params[i])); @@ -185,7 +204,9 @@ void wfx_stop(struct ieee80211_hw *hw) struct wfx_dev *wdev = hw->priv; wfx_tx_lock_flush(wdev); + mutex_lock(&wdev->conf_mutex); wfx_tx_queues_clear(wdev); + mutex_unlock(&wdev->conf_mutex); wfx_tx_unlock(wdev); WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked"); } diff --git a/drivers/staging/wfx/sta.h b/drivers/staging/wfx/sta.h index f36d94f907c7..dd1b6b3fc2f1 100644 --- a/drivers/staging/wfx/sta.h +++ b/drivers/staging/wfx/sta.h @@ -12,6 +12,8 @@ #include "hif_api_cmd.h" +struct wfx_vif; + struct wfx_edca_params { /* NOTE: index is a linux queue id. */ struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS]; @@ -29,4 +31,6 @@ void wfx_stop(struct ieee80211_hw *hw); int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable); + #endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index 322e46e343d5..a6b430ee7cdf 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -20,6 +20,7 @@ #include "queue.h" #include "secure_link.h" #include "sta.h" +#include "scan.h" #include "hif_tx.h" #include "hif_api_general.h" @@ -60,6 +61,7 @@ struct wfx_dev { struct wfx_hif hif; struct sl_context sl; int chip_frozen; + struct mutex conf_mutex; struct wfx_hif_cmd hif_cmd; struct wfx_queue tx_queue[4]; @@ -69,6 +71,9 @@ struct wfx_dev { struct hif_rx_stats rx_stats; struct mutex rx_stats_lock; + + int output_power; + atomic_t scan_in_progress; }; struct wfx_vif { @@ -92,11 +97,17 @@ struct wfx_vif { struct tx_policy_cache tx_policy_cache; struct work_struct tx_policy_upload_work; + u32 sta_asleep_mask; u32 pspoll_mask; spinlock_t ps_state_lock; + bool filter_bssid; + bool fwd_probe_req; + struct wfx_edca_params edca; + + struct wfx_scan scan; }; static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) From patchwork Thu Sep 19 10:52:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152145 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 69E6416B1 for ; Thu, 19 Sep 2019 10:53:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2958421929 for ; Thu, 19 Sep 2019 10:53:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="WUYtIv79" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389596AbfISKxj (ORCPT ); Thu, 19 Sep 2019 06:53:39 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389579AbfISKxE (ORCPT ); Thu, 19 Sep 2019 06:53:04 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=j16J/LUz2Ak4GKAtYvSjza4S/8DPCen8+As2uH0YnqR2GQE6C2FBwK70Hv/fVSAFWZol3MiQIiEqcwxmrOPo5cd//qqG+6ObJHZcFdRu9AIheCdq19fXftrKDJh2R61NmqpBW3tRjMYiX9FTyROx7A/Jtvt73ytP2Mj3zYGXu0qi/wVzdKepjeyiKrY9eN3A3WcgN0r/FnSnqZw8wxQ0HmtY+k0Jx2DFpOf98mIyBDSypoRnhmXCi7C9VvmMTaHomCGmbzwBLvvRjf+9FNiJMqJAb3630s68IxdxpD6ds6aerbapLN+fqiMbqXMDWG2FSW8K7B/q4cds1nNq1HSSwA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Iq0bAM9TxdbYc7Ea6czWlaAO/RkEBr8XvTZ9NGU1O+k=; b=AcU2uaK5ELhFvhpRsu8WSLd2wRSTaabQ2LfQtAQxKyv6k3FqLtTpwGMKrl43rYYh1vT4gy9ko9hvGQ4p39/nzjjnmJYZzFztMEohysBmk51oJ/Heawb+s+LYJRh+Bbyu7GLu6XYmw7H5EsAVTDN/CYUAWGWG9KNpi4csJjS/LIcrtIFsNvccy6PBsvKVY9Crk/ixIJ85GvL9q4+AJxTr/v/a1IfEoWSjAri71gIGS2r1y2U8a9A4UF/7NZt8ImGDiC8Y3sPJ0m2aQxv2YSF05Vk1CSAbEIUsCnFDqvUbWWVen+lgJSQafRzIP+qdlxB1CtUp/yuwGyRwHsAYIerQCQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Iq0bAM9TxdbYc7Ea6czWlaAO/RkEBr8XvTZ9NGU1O+k=; b=WUYtIv79teZ8ZpVXJl6SHckA83esLWOApbqyJGohplFyRP1G+C4Fup1qdPsSPsjr2ltebQWSLMunnJylm7v2zkDrDufrW4WGg8ZXYsbCLsNxvol8UTXdAJjbfI6erTTe4SiHIwwzcNc+8/hD3QSsirkCihqNbO3Fvi48uxBOdiY= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:45 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:45 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 19/20] staging: wfx: implement 802.11 key handling Thread-Topic: [PATCH 19/20] staging: wfx: implement 802.11 key handling Thread-Index: AQHVbthdFRXeeKeLB0ms1h9a98U3aA== Date: Thu, 19 Sep 2019 10:52:44 +0000 Message-ID: <20190919105153.15285-20-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 2d77b910-b970-4e35-89c3-08d73cef8122 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:2657; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(30864003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: qUBmXdBD+e3jZITWRN3c20/2M1VLzj6kQ1Nq/dh3Jg9jjd4Q8iITOOAuShMQbWtXhj82/oi+IPi74c4rP7ZhObDUv/dQVMYFsbLzc9xx8O/JzbrO+PXN2COPq4uG7bXW9D3F62ULpdE35AZiVwqiUSAVrr9hBLFdoM+liiKPf5/MW6+wFLkIOjf0Bf3O0Dlf0qt3EJ/DbNt+XdRhXRZXb0Li+3Sn93G8g/ARkrmAqtfn6Qxax8BmmPjwP4DK/KEobb8J7QCek9FJGluyJ7WSrSAFE7hGi+L3F6gJsdAc/CSIetExX8OpI21AYmR2fb9Q7y8aBHZ/lU5eqkVlzC+zuNe8krlA4SN2OAvyGDEXe4KLazGizqXYDS8mC8Vbe2ozuEEJVo9hIAKGayQ9mhOfW600TUzf3+I3emonJ7GE7hg= Content-ID: <5AFB49AAC013034F95A827B3770F279D@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2d77b910-b970-4e35-89c3-08d73cef8122 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:44.2587 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: W4x7DNqNZ55CaQFAAUrCCrfC6EemKvtdB2nkWCGWd4Nnwyx+TrFzsAHt+Sa+HTkpEy+m6K+9asvddAuLF/+VEg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/Makefile | 1 + drivers/staging/wfx/key.c | 272 +++++++++++++++++++++++++++++++++++ drivers/staging/wfx/key.h | 22 +++ drivers/staging/wfx/main.c | 2 + drivers/staging/wfx/sta.c | 4 + drivers/staging/wfx/wfx.h | 19 +++ 6 files changed, 320 insertions(+) create mode 100644 drivers/staging/wfx/key.c create mode 100644 drivers/staging/wfx/key.h diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile index 2b8a5fa86fac..0d9c1ed092f6 100644 --- a/drivers/staging/wfx/Makefile +++ b/drivers/staging/wfx/Makefile @@ -14,6 +14,7 @@ wfx-y := \ data_rx.o \ scan.o \ sta.o \ + key.o \ main.o \ sta.o \ debug.o diff --git a/drivers/staging/wfx/key.c b/drivers/staging/wfx/key.c new file mode 100644 index 000000000000..696424f244cb --- /dev/null +++ b/drivers/staging/wfx/key.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Key management related functions. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#include +#include + +#include "key.h" +#include "wfx.h" +#include "hif_tx_mib.h" + +static int wfx_alloc_key(struct wfx_dev *wdev) +{ + int idx; + + idx = ffs(~wdev->key_map) - 1; + if (idx < 0 || idx >= MAX_KEY_ENTRIES) + return -1; + + wdev->key_map |= BIT(idx); + wdev->keys[idx].entry_index = idx; + return idx; +} + +static void wfx_free_key(struct wfx_dev *wdev, int idx) +{ + BUG_ON(!(wdev->key_map & BIT(idx))); + memset(&wdev->keys[idx], 0, sizeof(wdev->keys[idx])); + wdev->key_map &= ~BIT(idx); +} + +static uint8_t fill_wep_pair(struct hif_wep_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + WARN_ON(key->keylen > sizeof(msg->key_data)); + msg->key_length = key->keylen; + memcpy(msg->key_data, key->key, key->keylen); + ether_addr_copy(msg->peer_address, peer_addr); + return HIF_KEY_TYPE_WEP_PAIRWISE; +} + +static uint8_t fill_wep_group(struct hif_wep_group_key *msg, + struct ieee80211_key_conf *key) +{ + WARN_ON(key->keylen > sizeof(msg->key_data)); + msg->key_id = key->keyidx; + msg->key_length = key->keylen; + memcpy(msg->key_data, key->key, key->keylen); + return HIF_KEY_TYPE_WEP_DEFAULT; +} + +static uint8_t fill_tkip_pair(struct hif_tkip_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + uint8_t *keybuf = key->key; + + WARN_ON(key->keylen != sizeof(msg->tkip_key_data) + + sizeof(msg->tx_mic_key) + + sizeof(msg->rx_mic_key)); + memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data)); + keybuf += sizeof(msg->tkip_key_data); + memcpy(msg->tx_mic_key, keybuf, sizeof(msg->tx_mic_key)); + keybuf += sizeof(msg->tx_mic_key); + memcpy(msg->rx_mic_key, keybuf, sizeof(msg->rx_mic_key)); + ether_addr_copy(msg->peer_address, peer_addr); + return HIF_KEY_TYPE_TKIP_PAIRWISE; +} + +static uint8_t fill_tkip_group(struct hif_tkip_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq, + enum nl80211_iftype iftype) +{ + uint8_t *keybuf = key->key; + + WARN_ON(key->keylen != sizeof(msg->tkip_key_data) + + 2 * sizeof(msg->rx_mic_key)); + msg->key_id = key->keyidx; + memcpy(msg->rx_sequence_counter, &seq->tkip.iv16, sizeof(seq->tkip.iv16)); + memcpy(msg->rx_sequence_counter + sizeof(uint16_t), &seq->tkip.iv32, sizeof(seq->tkip.iv32)); + memcpy(msg->tkip_key_data, keybuf, sizeof(msg->tkip_key_data)); + keybuf += sizeof(msg->tkip_key_data); + if (iftype == NL80211_IFTYPE_AP) + // Use Tx MIC Key + memcpy(msg->rx_mic_key, keybuf + 0, sizeof(msg->rx_mic_key)); + else + // Use Rx MIC Key + memcpy(msg->rx_mic_key, keybuf + 8, sizeof(msg->rx_mic_key)); + return HIF_KEY_TYPE_TKIP_GROUP; +} + +static uint8_t fill_ccmp_pair(struct hif_aes_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + WARN_ON(key->keylen != sizeof(msg->aes_key_data)); + ether_addr_copy(msg->peer_address, peer_addr); + memcpy(msg->aes_key_data, key->key, key->keylen); + return HIF_KEY_TYPE_AES_PAIRWISE; +} + +static uint8_t fill_ccmp_group(struct hif_aes_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + WARN_ON(key->keylen != sizeof(msg->aes_key_data)); + memcpy(msg->aes_key_data, key->key, key->keylen); + memcpy(msg->rx_sequence_counter, seq->ccmp.pn, sizeof(seq->ccmp.pn)); + memreverse(msg->rx_sequence_counter, sizeof(seq->ccmp.pn)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_AES_GROUP; +} + +static uint8_t fill_sms4_pair(struct hif_wapi_pairwise_key *msg, + struct ieee80211_key_conf *key, u8 *peer_addr) +{ + uint8_t *keybuf = key->key; + + WARN_ON(key->keylen != sizeof(msg->wapi_key_data) + + sizeof(msg->mic_key_data)); + ether_addr_copy(msg->peer_address, peer_addr); + memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data)); + keybuf += sizeof(msg->wapi_key_data); + memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_WAPI_PAIRWISE; +} + +static uint8_t fill_sms4_group(struct hif_wapi_group_key *msg, + struct ieee80211_key_conf *key) +{ + uint8_t *keybuf = key->key; + + WARN_ON(key->keylen != sizeof(msg->wapi_key_data) + + sizeof(msg->mic_key_data)); + memcpy(msg->wapi_key_data, keybuf, sizeof(msg->wapi_key_data)); + keybuf += sizeof(msg->wapi_key_data); + memcpy(msg->mic_key_data, keybuf, sizeof(msg->mic_key_data)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_WAPI_GROUP; +} + +static uint8_t fill_aes_cmac_group(struct hif_igtk_group_key *msg, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + WARN_ON(key->keylen != sizeof(msg->igtk_key_data)); + memcpy(msg->igtk_key_data, key->key, key->keylen); + memcpy(msg->ipn, seq->aes_cmac.pn, sizeof(seq->aes_cmac.pn)); + memreverse(msg->ipn, sizeof(seq->aes_cmac.pn)); + msg->key_id = key->keyidx; + return HIF_KEY_TYPE_IGTK_GROUP; +} + +static int wfx_add_key(struct wfx_vif *wvif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret; + struct hif_req_add_key *k; + struct ieee80211_key_seq seq; + struct wfx_dev *wdev = wvif->wdev; + int idx = wfx_alloc_key(wvif->wdev); + bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE; + + WARN_ON(key->flags & IEEE80211_KEY_FLAG_PAIRWISE && !sta); + ieee80211_get_key_rx_seq(key, 0, &seq); + if (idx < 0) + return -EINVAL; + k = &wdev->keys[idx]; + k->int_id = wvif->id; + if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) { + if (pairwise) + k->type = fill_wep_pair(&k->key.wep_pairwise_key, key, sta->addr); + else + k->type = fill_wep_group(&k->key.wep_group_key, key); + } else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { + if (pairwise) + k->type = fill_tkip_pair(&k->key.tkip_pairwise_key, key, sta->addr); + else + k->type = fill_tkip_group(&k->key.tkip_group_key, key, &seq, wvif->vif->type); + } else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) { + if (pairwise) + k->type = fill_ccmp_pair(&k->key.aes_pairwise_key, key, sta->addr); + else + k->type = fill_ccmp_group(&k->key.aes_group_key, key, &seq); + } else if (key->cipher == WLAN_CIPHER_SUITE_SMS4) { + if (pairwise) + k->type = fill_sms4_pair(&k->key.wapi_pairwise_key, key, sta->addr); + else + k->type = fill_sms4_group(&k->key.wapi_group_key, key); + } else if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + k->type = fill_aes_cmac_group(&k->key.igtk_group_key, key, &seq); + } else { + dev_warn(wdev->dev, "unsupported key type %d\n", key->cipher); + wfx_free_key(wdev, idx); + return -EOPNOTSUPP; + } + ret = hif_add_key(wdev, k); + if (ret) { +#if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE && \ + KERNEL_VERSION(4, 9, 63) > LINUX_VERSION_CODE && \ + KERNEL_VERSION(4, 4, 99) > LINUX_VERSION_CODE + if (ret == HIF_INVALID_PARAMETER) { + // Use a patched kernel in order to solve this error + dev_warn(wdev->dev, "chip prevents re-installation of same key\n"); + dev_warn(wdev->dev, "your kernel is not patched to protect against KRACK attack\n"); + } +#endif + wfx_free_key(wdev, idx); + return -EOPNOTSUPP; + } +#if (KERNEL_VERSION(3, 19, 0) > LINUX_VERSION_CODE) + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; +#else + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; +#endif + key->hw_key_idx = idx; + return 0; +} + +static int wfx_remove_key(struct wfx_vif *wvif, struct ieee80211_key_conf *key) +{ + WARN(key->hw_key_idx >= MAX_KEY_ENTRIES, "corrupted hw_key_idx"); + wfx_free_key(wvif->wdev, key->hw_key_idx); + return hif_remove_key(wvif->wdev, key->hw_key_idx); +} + +int wfx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + + mutex_lock(&wvif->wdev->conf_mutex); + if (cmd == SET_KEY) + ret = wfx_add_key(wvif, sta, key); + if (cmd == DISABLE_KEY) + ret = wfx_remove_key(wvif, key); + mutex_unlock(&wvif->wdev->conf_mutex); + return ret; +} + +int wfx_upload_keys(struct wfx_vif *wvif) +{ + int i; + struct hif_req_add_key *key; + struct wfx_dev *wdev = wvif->wdev; + + for (i = 0; i < ARRAY_SIZE(wdev->keys); i++) { + if (wdev->key_map & BIT(i)) { + key = &wdev->keys[i]; + if (key->int_id == wvif->id) + hif_add_key(wdev, key); + } + } + return 0; +} + +void wfx_wep_key_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, wep_key_work); + + wfx_tx_flush(wvif->wdev); + hif_wep_default_key_id(wvif, wvif->wep_default_key_id); + wfx_pending_requeue(wvif->wdev, wvif->wep_pending_skb); + wvif->wep_pending_skb = NULL; + wfx_tx_unlock(wvif->wdev); +} diff --git a/drivers/staging/wfx/key.h b/drivers/staging/wfx/key.h new file mode 100644 index 000000000000..9436ccdf4d3b --- /dev/null +++ b/drivers/staging/wfx/key.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Implementation of mac80211 API. + * + * Copyright (c) 2017-2019, Silicon Laboratories, Inc. + * Copyright (c) 2010, ST-Ericsson + */ +#ifndef WFX_KEY_H +#define WFX_KEY_H + +#include + +struct wfx_dev; +struct wfx_vif; + +int wfx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int wfx_upload_keys(struct wfx_vif *wvif); +void wfx_wep_key_work(struct work_struct *work); + +#endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 06220bac5b75..e7bba24aae0b 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -27,6 +27,7 @@ #include "bus.h" #include "bh.h" #include "sta.h" +#include "key.h" #include "debug.h" #include "data_tx.h" #include "secure_link.h" @@ -56,6 +57,7 @@ static const struct ieee80211_ops wfx_ops = { .remove_interface = wfx_remove_interface, .tx = wfx_tx, .hw_scan = wfx_hw_scan, + .set_key = wfx_set_key, }; bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index c9a35a5307dd..ccf45bdb7e42 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -10,6 +10,7 @@ #include "sta.h" #include "wfx.h" +#include "key.h" #include "scan.h" #include "hif_tx_mib.h" @@ -172,6 +173,9 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); #endif + wvif->wep_default_key_id = -1; + INIT_WORK(&wvif->wep_key_work, wfx_wep_key_work); + sema_init(&wvif->scan.lock, 1); INIT_WORK(&wvif->scan.work, wfx_scan_work); INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout); diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index a6b430ee7cdf..ba5e1a32d869 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -69,6 +69,9 @@ struct wfx_dev { int tx_burst_idx; atomic_t tx_lock; + u32 key_map; + struct hif_req_add_key keys[MAX_KEY_ENTRIES]; + struct hif_rx_stats rx_stats; struct mutex rx_stats_lock; @@ -94,6 +97,9 @@ struct wfx_vif { struct work_struct mcast_start_work; struct work_struct mcast_stop_work; + s8 wep_default_key_id; + struct sk_buff *wep_pending_skb; + struct work_struct wep_key_work; struct tx_policy_cache tx_policy_cache; struct work_struct tx_policy_upload_work; @@ -141,6 +147,19 @@ static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev, struct wfx_vif return NULL; } +static inline void memreverse(uint8_t *src, uint8_t length) +{ + uint8_t *lo = src; + uint8_t *hi = src + length - 1; + uint8_t swap; + + while (lo < hi) { + swap = *lo; + *lo++ = *hi; + *hi-- = swap; + } +} + static inline int memzcmp(void *src, unsigned int size) { uint8_t *buf = src; From patchwork Thu Sep 19 10:52:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgUG91aWxsZXI=?= X-Patchwork-Id: 11152143 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1059716B1 for ; Thu, 19 Sep 2019 10:53:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A21D921924 for ; Thu, 19 Sep 2019 10:53:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="jHUe1YnV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389666AbfISKxU (ORCPT ); Thu, 19 Sep 2019 06:53:20 -0400 Received: from mail-eopbgr730082.outbound.protection.outlook.com ([40.107.73.82]:1632 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2389596AbfISKxN (ORCPT ); Thu, 19 Sep 2019 06:53:13 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=dkfZ9IkH1szwmuliMKtySAuVPaPUjZmcblJR1cFyGZWq4Uxz8jk+zVNpRFCcRRXv5pSi/5TlRQemBb7tCzPHrEEco7iSzRrPcNSSPowic9Xby619mcgkcnJXk1Lhhvfa+ReKsVRV1yg9OB2VupFFYe2ZP5B0GA5tiYZamkhDUqacaKCef0Iw8FeZ96Z7uDzBaK9jK/J+MzKv4pbSgutRFLzYN3UYf5/ZXfdb2zmtTJQ/tBK4Mudz0Ye+WExGISx0Oa7voexdI4b8NUrdHBYVw24NjlrYIMrWk9v41WKBA2aGOBJqVUI6c2z6PaPnxCOVbw1+JZONaCkl+1ZBzwQ8KQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Sf3aFPreAFmK8X8gCaDkkhXHeFuduszHvMizxWgML88=; b=ZU4LRhQKaPussh99z8GCr/7VkP5vJp/3cPV5A1sp6es7QZtEJl6MrSAuZYhr8Mb5efTa15T9YsKQKRVjFoY6jHgmm1BKWZPbzR4OBaDOz5i7oMR59HpdSnFQ4mudaoj8JEpBUbJQYx3hLYRulojF2qlLJDFRf222JQqorB5umkDDwWKYvmohQejvZ9UkQdNJDzysQTchXVmNAC2phUmHjO+FOTlnmgD5aQ97JtWH6ZFR4+KkebZISs6+OhCvlifEk+cIhGIsEhFMx9ybbQlaEOuYHdTM6Sjn+UUfCCIx30w4rpsZ5zzGzgIVhX8T38OoZlFsr6bPQclw77LhljyWFw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=silabs.com; dmarc=pass action=none header.from=silabs.com; dkim=pass header.d=silabs.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=silabs.onmicrosoft.com; s=selector2-silabs-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Sf3aFPreAFmK8X8gCaDkkhXHeFuduszHvMizxWgML88=; b=jHUe1YnV+wyqJX7kJt30p72BHxltVDJ/DKnX/x4E3c2AJqRmFBCIf0m3yef8KtIV5Kzy1BWU/WYHdFTxwVXh22ojJjEEJuaXc40TCOniihuho2d3aaLJapI3WUHCT+RNzegPYdPY5FK1AuJgSAg02It6aPpIkzR4JYV9Vg9P/fE= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4415.namprd11.prod.outlook.com (52.135.39.95) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2263.17; Thu, 19 Sep 2019 10:52:46 +0000 Received: from MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8]) by MN2PR11MB4063.namprd11.prod.outlook.com ([fe80::45dc:e073:4446:4bf8%3]) with mapi id 15.20.2263.023; Thu, 19 Sep 2019 10:52:46 +0000 From: Jerome Pouiller To: "devel@driverdev.osuosl.org" , "linux-wireless@vger.kernel.org" CC: "netdev@vger.kernel.org" , "linux-kernel@vger.kernel.org" , Greg Kroah-Hartman , Kalle Valo , "David S . Miller" , David Le Goff , Jerome Pouiller Subject: [PATCH 20/20] staging: wfx: implement the rest of mac80211 API Thread-Topic: [PATCH 20/20] staging: wfx: implement the rest of mac80211 API Thread-Index: AQHVbtheGs4T+FlEkE6qtw/D5YowOA== Date: Thu, 19 Sep 2019 10:52:44 +0000 Message-ID: <20190919105153.15285-21-Jerome.Pouiller@silabs.com> References: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=Jerome.Pouiller@silabs.com; x-originating-ip: [37.71.187.125] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 0c7e0da2-b5cb-4a42-12ee-08d73cef817a x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(5600167)(711020)(4605104)(1401327)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7193020);SRVR:MN2PR11MB4415; x-ms-traffictypediagnostic: MN2PR11MB4415: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-ms-oob-tlc-oobclassifiers: OLM:117; x-forefront-prvs: 016572D96D x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(1496009)(346002)(396003)(366004)(39850400004)(376002)(136003)(189003)(199004)(14454004)(71190400001)(2906002)(81156014)(81166006)(478600001)(316002)(2501003)(64756008)(76176011)(66476007)(256004)(86362001)(66446008)(99286004)(25786009)(8676002)(66556008)(66946007)(11346002)(6512007)(110136005)(66574012)(5660300002)(54906003)(30864003)(6436002)(71200400001)(14444005)(76116006)(3846002)(6506007)(91956017)(1076003)(6116002)(486006)(305945005)(36756003)(2616005)(446003)(4326008)(7736002)(8936002)(66066001)(186003)(107886003)(26005)(476003)(6486002)(102836004)(569006);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4415;H:MN2PR11MB4063.namprd11.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;MX:1;A:1; received-spf: None (protection.outlook.com: silabs.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: 3wXD8OlS5Y4SCtMuwOvJQS1WTYK3QeEwcef9bMZ28LhGI0UY+uje9lDpAltfr+f8F5Vj3X57wvuip7UbmLXw4Iv7RP0i/fB235r45Fqjbqv+vV21Ec6VbkZ5hn8yHhnuODHhVa98qHJhg+3qVQwUJgQmDX56pZnKkKQr7v59OQqeOZeQys3zTV/OOYkbvj5htWsYARRQaTsbTr1dDp67hUr6OT8/YMawlvp2aNdByeF4si3fVM4ViEkAo64nCEb24KDc6GZyw5Yc9TQNOHb36SptjJesZlq07aSd8eiQlSBCC7Wrp3miIFhvbm8wXehW7Us/MwL4tno+z62VmiAxet/eSvF0BpvQDlxFwwmqsGmJr6HFxJi4RhbiA0PnkNmy05XjrsyiiPBtjBmfRNm6fp/Tzu1+wFA6HMeB0qWnFBQ= Content-ID: <0686155FF638104FBD0FA28E9CD46A56@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0c7e0da2-b5cb-4a42-12ee-08d73cef817a X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 10:52:44.8604 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 54dbd822-5231-4b20-944d-6f4abcd541fb X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: 1PpX95/3VBv4i2ZmwIFmHLzOTQiBIMXDw9WcB16lSdh7Se2moo4gYf5bg1LCUJlxEgMDCzLmH0ZSgR7nUVj4dg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4415 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Jérôme Pouiller Signed-off-by: Jérôme Pouiller --- drivers/staging/wfx/data_rx.c | 26 + drivers/staging/wfx/data_tx.c | 16 + drivers/staging/wfx/debug.c | 2 + drivers/staging/wfx/hif_rx.c | 53 ++ drivers/staging/wfx/hif_tx.c | 1 + drivers/staging/wfx/main.c | 137 +++ drivers/staging/wfx/queue.c | 80 ++ drivers/staging/wfx/scan.c | 40 + drivers/staging/wfx/sta.c | 1472 ++++++++++++++++++++++++++++++++- drivers/staging/wfx/sta.h | 80 ++ drivers/staging/wfx/wfx.h | 60 ++ 11 files changed, 1964 insertions(+), 3 deletions(-) diff --git a/drivers/staging/wfx/data_rx.c b/drivers/staging/wfx/data_rx.c index 6544d00d1657..0d73b52eee36 100644 --- a/drivers/staging/wfx/data_rx.c +++ b/drivers/staging/wfx/data_rx.c @@ -22,6 +22,8 @@ static int wfx_handle_pspoll(struct wfx_vif *wvif, struct sk_buff *skb) u32 pspoll_mask = 0; int i; + if (wvif->state != WFX_STATE_AP) + return 1; if (!ether_addr_equal(wvif->vif->addr, pspoll->bssid)) return 1; @@ -167,6 +169,30 @@ void wfx_rx_cb(struct wfx_vif *wvif, struct hif_ind_rx *arg, struct sk_buff *skb && arg->rx_flags.match_uc_addr && mgmt->u.action.category == WLAN_CATEGORY_BACK) goto drop; + if (ieee80211_is_beacon(frame->frame_control) + && !arg->status && wvif->vif + && ether_addr_equal(ieee80211_get_SA(frame), wvif->vif->bss_conf.bssid)) { + const u8 *tim_ie; + u8 *ies = mgmt->u.beacon.variable; + size_t ies_len = skb->len - (ies - skb->data); + + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); + if (tim_ie) { + struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) &tim_ie[2]; + + if (wvif->dtim_period != tim->dtim_period) { + wvif->dtim_period = tim->dtim_period; + schedule_work(&wvif->set_beacon_wakeup_period_work); + } + } + + /* Disable beacon filter once we're associated... */ + if (wvif->disable_beacon_filter && + (wvif->vif->bss_conf.assoc || wvif->vif->bss_conf.ibss_joined)) { + wvif->disable_beacon_filter = false; + schedule_work(&wvif->update_filtering_work); + } + } if (early_data) { spin_lock_bh(&wvif->ps_state_lock); diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index 217d3c270706..7f2799fbdafe 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -10,6 +10,7 @@ #include "data_tx.h" #include "wfx.h" #include "bh.h" +#include "sta.h" #include "queue.h" #include "debug.h" #include "traces.h" @@ -359,6 +360,9 @@ void wfx_link_id_gc_work(struct work_struct *work) u32 mask; int i; + if (wvif->state != WFX_STATE_AP) + return; + wfx_tx_lock_flush(wvif->wdev); spin_lock_bh(&wvif->ps_state_lock); for (i = 0; i < WFX_MAX_STA_IN_AP_MODE; ++i) { @@ -729,14 +733,26 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg) memset(tx_info->pad, 0, sizeof(tx_info->pad)); if (!arg->status) { + if (wvif->bss_loss_state && arg->packet_id == wvif->bss_loss_confirm_id) + wfx_cqm_bssloss_sm(wvif, 0, 1, 0); tx_info->status.tx_time = arg->media_delay - arg->tx_queue_delay; if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; else tx_info->flags |= IEEE80211_TX_STAT_ACK; } else if (arg->status == HIF_REQUEUE) { + /* "REQUEUE" means "implicit suspend" */ + struct hif_ind_suspend_resume_tx suspend = { + .suspend_resume_flags.resume = 0, + .suspend_resume_flags.bc_mc_only = 1, + }; + WARN(!arg->tx_result_flags.requeue, "incoherent status and result_flags"); + wfx_suspend_resume(wvif, &suspend); tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + } else { + if (wvif->bss_loss_state && arg->packet_id == wvif->bss_loss_confirm_id) + wfx_cqm_bssloss_sm(wvif, 0, 0, 1); } wfx_pending_remove(wvif->wdev, skb); } diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c index 4bd9a079cbd9..14642471f4a9 100644 --- a/drivers/staging/wfx/debug.c +++ b/drivers/staging/wfx/debug.c @@ -12,7 +12,9 @@ #include "debug.h" #include "wfx.h" +#include "sta.h" #include "main.h" +#include "hif_tx.h" #include "hif_tx_mib.h" #define CREATE_TRACE_POINTS diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c index d386fab0a90f..52db02d3aa41 100644 --- a/drivers/staging/wfx/hif_rx.c +++ b/drivers/staging/wfx/hif_rx.c @@ -12,6 +12,8 @@ #include "hif_rx.h" #include "wfx.h" #include "scan.h" +#include "bh.h" +#include "sta.h" #include "data_rx.h" #include "secure_link.h" #include "hif_api_cmd.h" @@ -144,6 +146,43 @@ static int hif_receive_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi return 0; } +static int hif_event_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_event *body = buf; + struct wfx_hif_event *event; + int first; + + WARN_ON(!wvif); + if (!wvif) + return 0; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + memcpy(&event->evt, body, sizeof(struct hif_ind_event)); + spin_lock(&wvif->event_queue_lock); + first = list_empty(&wvif->event_queue); + list_add_tail(&event->link, &wvif->event_queue); + spin_unlock(&wvif->event_queue_lock); + + if (first) + schedule_work(&wvif->event_handler_work); + + return 0; +} + +static int hif_pm_mode_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + + WARN_ON(!wvif); + complete(&wvif->set_pm_mode_complete); + + return 0; +} + static int hif_scan_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); @@ -165,6 +204,17 @@ static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hi return 0; } +static int hif_suspend_resume_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) +{ + struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface); + struct hif_ind_suspend_resume_tx *body = buf; + + WARN_ON(!wvif); + wfx_suspend_resume(wvif, body); + + return 0; +} + static int hif_error_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf) { struct hif_ind_error *body = buf; @@ -242,8 +292,11 @@ static const struct { { HIF_IND_ID_STARTUP, hif_startup_indication }, { HIF_IND_ID_WAKEUP, hif_wakeup_indication }, { HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication }, + { HIF_IND_ID_SET_PM_MODE_CMPL, hif_pm_mode_complete_indication }, { HIF_IND_ID_SCAN_CMPL, hif_scan_complete_indication }, + { HIF_IND_ID_SUSPEND_RESUME_TX, hif_suspend_resume_indication }, { HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication }, + { HIF_IND_ID_EVENT, hif_event_indication }, { HIF_IND_ID_GENERIC, hif_generic_indication }, { HIF_IND_ID_ERROR, hif_error_indication }, { HIF_IND_ID_EXCEPTION, hif_exception_indication }, diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c index 157ab177b73f..2d40225a0fce 100644 --- a/drivers/staging/wfx/hif_tx.c +++ b/drivers/staging/wfx/hif_tx.c @@ -14,6 +14,7 @@ #include "bh.h" #include "hwio.h" #include "debug.h" +#include "sta.h" void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd) { diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index e7bba24aae0b..626615a7d3c4 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -10,6 +10,7 @@ * Copyright (c) 2006, Michael Wu * Copyright (c) 2004-2006 Jean-Baptiste Note , et al. */ +#include #include #include #include @@ -50,14 +51,112 @@ static char *slk_key; module_param(slk_key, charp, 0600); MODULE_PARM_DESC(slk_key, "secret key for secure link (expect 64 hexdecimal digits)."); +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ +} + +static struct ieee80211_rate wfx_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(55, 2, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(110, 3, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel wfx_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_supported_band wfx_band_2ghz = { + .channels = wfx_2ghz_chantable, + .n_channels = ARRAY_SIZE(wfx_2ghz_chantable), + .bitrates = wfx_rates, + .n_bitrates = ARRAY_SIZE(wfx_rates), + .ht_cap = { + // Receive caps + .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_MAX_AMSDU | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask = { 0xFF }, // MCS0 to MCS7 + .rx_highest = 65, + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const struct ieee80211_iface_limit wdev_iface_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination wfx_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 2, + .limits = wdev_iface_limits, + .n_limits = ARRAY_SIZE(wdev_iface_limits), + } +}; + static const struct ieee80211_ops wfx_ops = { .start = wfx_start, .stop = wfx_stop, .add_interface = wfx_add_interface, .remove_interface = wfx_remove_interface, + .config = wfx_config, .tx = wfx_tx, + .conf_tx = wfx_conf_tx, .hw_scan = wfx_hw_scan, + .sta_add = wfx_sta_add, + .sta_remove = wfx_sta_remove, + .sta_notify = wfx_sta_notify, + .set_tim = wfx_set_tim, .set_key = wfx_set_key, + .set_rts_threshold = wfx_set_rts_threshold, + .bss_info_changed = wfx_bss_info_changed, + .prepare_multicast = wfx_prepare_multicast, + .configure_filter = wfx_configure_filter, + .ampdu_action = wfx_ampdu_action, + .flush = wfx_flush, + .add_chanctx = wfx_add_chanctx, + .remove_chanctx = wfx_remove_chanctx, + .change_chanctx = wfx_change_chanctx, + .assign_vif_chanctx = wfx_assign_vif_chanctx, + .unassign_vif_chanctx = wfx_unassign_vif_chanctx, }; bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) @@ -198,6 +297,19 @@ struct wfx_dev *wfx_init_common(struct device *dev, SET_IEEE80211_DEV(hw, dev); + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, MFP_CAPABLE); +#if (KERNEL_VERSION(3, 19, 0) > LINUX_VERSION_CODE) + ieee80211_hw_set(hw, SUPPORTS_UAPSD); +#endif + hw->vif_data_size = sizeof(struct wfx_vif); hw->sta_data_size = sizeof(struct wfx_sta_priv); hw->queues = 4; @@ -206,8 +318,19 @@ struct wfx_dev *wfx_init_common(struct device *dev, hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg) + sizeof(struct hif_req_tx) + 4 /* alignment */ + 8 /* TKIP IV */; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + hw->wiphy->max_ap_assoc_sta = WFX_MAX_STA_IN_AP_MODE; hw->wiphy->max_scan_ssids = 2; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(wfx_iface_combinations); + hw->wiphy->iface_combinations = wfx_iface_combinations; + hw->wiphy->bands[NL80211_BAND_2GHZ] = devm_kmalloc(dev, sizeof(wfx_band_2ghz), GFP_KERNEL); + // FIXME: also copy wfx_rates and wfx_2ghz_chantable + memcpy(hw->wiphy->bands[NL80211_BAND_2GHZ], &wfx_band_2ghz, sizeof(wfx_band_2ghz)); wdev = hw->priv; wdev->hw = hw; @@ -290,6 +413,12 @@ int wfx_probe(struct wfx_dev *wdev) goto err1; } + if (wdev->hw_caps.regul_sel_mode_info.region_sel_mode) { + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[11].flags |= IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[12].flags |= IEEE80211_CHAN_NO_IR; + wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[13].flags |= IEEE80211_CHAN_DISABLED; + } + dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds); err = wfx_send_pdata_pds(wdev); if (err < 0) @@ -322,6 +451,12 @@ int wfx_probe(struct wfx_dev *wdev) } dev_info(wdev->dev, "MAC address %d: %pM\n", i, wdev->addresses[i].addr); } + wdev->hw->wiphy->n_addresses = ARRAY_SIZE(wdev->addresses); + wdev->hw->wiphy->addresses = wdev->addresses; + + err = ieee80211_register_hw(wdev->hw); + if (err) + goto err1; err = wfx_debug_init(wdev); if (err) @@ -330,6 +465,7 @@ int wfx_probe(struct wfx_dev *wdev) return 0; err2: + ieee80211_unregister_hw(wdev->hw); ieee80211_free_hw(wdev->hw); err1: wfx_bh_unregister(wdev); @@ -338,6 +474,7 @@ int wfx_probe(struct wfx_dev *wdev) void wfx_release(struct wfx_dev *wdev) { + ieee80211_unregister_hw(wdev->hw); hif_shutdown(wdev); wfx_bh_unregister(wdev); wfx_sl_deinit(wdev); diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c index aa438be21d37..6f1be4f6f463 100644 --- a/drivers/staging/wfx/queue.c +++ b/drivers/staging/wfx/queue.c @@ -351,6 +351,83 @@ bool wfx_tx_queues_is_empty(struct wfx_dev *wdev) return ret; } +static bool hif_handle_tx_data(struct wfx_vif *wvif, struct sk_buff *skb, + struct wfx_queue *queue) +{ + bool handled = false; + struct wfx_tx_priv *tx_priv = wfx_skb_tx_priv(skb); + struct hif_req_tx *req = wfx_skb_txreq(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *) (req->frame + req->data_flags.fc_offset); + + enum { + do_probe, + do_drop, + do_wep, + do_tx, + } action = do_tx; + + switch (wvif->vif->type) { + case NL80211_IFTYPE_STATION: + if (wvif->state < WFX_STATE_PRE_STA) + action = do_drop; + break; + case NL80211_IFTYPE_AP: + if (!wvif->state) { + action = do_drop; + } else if (!(BIT(tx_priv->raw_link_id) & (BIT(0) | wvif->link_id_map))) { + dev_warn(wvif->wdev->dev, "a frame with expired link-id is dropped\n"); + action = do_drop; + } + break; + case NL80211_IFTYPE_ADHOC: + if (wvif->state != WFX_STATE_IBSS) + action = do_drop; + break; + case NL80211_IFTYPE_MONITOR: + default: + action = do_drop; + break; + } + + if (action == do_tx) { + if (ieee80211_is_nullfunc(frame->frame_control)) { + mutex_lock(&wvif->bss_loss_lock); + if (wvif->bss_loss_state) { + wvif->bss_loss_confirm_id = req->packet_id; + req->queue_id.queue_id = HIF_QUEUE_ID_VOICE; + } + mutex_unlock(&wvif->bss_loss_lock); + } else if (ieee80211_has_protected(frame->frame_control) && + tx_priv->hw_key && + tx_priv->hw_key->keyidx != wvif->wep_default_key_id && + (tx_priv->hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || + tx_priv->hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { + action = do_wep; + } + } + + switch (action) { + case do_drop: + BUG_ON(wfx_pending_remove(wvif->wdev, skb)); + handled = true; + break; + case do_wep: + wfx_tx_lock(wvif->wdev); + wvif->wep_default_key_id = tx_priv->hw_key->keyidx; + wvif->wep_pending_skb = skb; + if (!schedule_work(&wvif->wep_key_work)) + wfx_tx_unlock(wvif->wdev); + handled = true; + break; + case do_tx: + break; + default: + /* Do nothing */ + break; + } + return handled; +} + static int wfx_get_prio_queue(struct wfx_vif *wvif, u32 tx_allowed_mask, int *total) { @@ -498,6 +575,9 @@ struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev) wvif = wdev_to_wvif(wdev, hif->interface); WARN_ON(!wvif); + if (hif_handle_tx_data(wvif, skb, queue)) + continue; /* Handled by WSM */ + wvif->pspoll_mask &= ~BIT(tx_priv->raw_link_id); /* allow bursting if txop is set */ diff --git a/drivers/staging/wfx/scan.c b/drivers/staging/wfx/scan.c index 89af294cf23d..07f800b92260 100644 --- a/drivers/staging/wfx/scan.c +++ b/drivers/staging/wfx/scan.c @@ -26,11 +26,26 @@ static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool abor #endif } +static void wfx_scan_restart_delayed(struct wfx_vif *wvif) +{ + if (wvif->delayed_unjoin) { + wvif->delayed_unjoin = false; + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wvif->wdev); + } else if (wvif->delayed_link_loss) { + wvif->delayed_link_loss = 0; + wfx_cqm_bssloss_sm(wvif, 1, 0, 0); + } +} + static int wfx_scan_start(struct wfx_vif *wvif, struct wfx_scan_params *scan) { int ret; int tmo = 500; + if (wvif->state == WFX_STATE_PRE_STA) + return -EBUSY; + tmo += scan->scan_req.num_of_channels * ((20 * (scan->scan_req.max_channel_time)) + 10); atomic_set(&wvif->scan.in_progress, 1); @@ -43,6 +58,7 @@ static int wfx_scan_start(struct wfx_vif *wvif, struct wfx_scan_params *scan) atomic_set(&wvif->scan.in_progress, 0); atomic_set(&wvif->wdev->scan_in_progress, 0); cancel_delayed_work_sync(&wvif->scan.timeout); + wfx_scan_restart_delayed(wvif); } return ret; } @@ -61,6 +77,9 @@ int wfx_hw_scan(struct ieee80211_hw *hw, if (!wvif) return -EINVAL; + if (wvif->state == WFX_STATE_AP) + return -EOPNOTSUPP; + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) req->n_ssids = 0; @@ -130,11 +149,23 @@ void wfx_scan_work(struct work_struct *work) .scan_req.scan_type.type = 0, /* Foreground */ }; struct ieee80211_channel *first; + bool first_run = (wvif->scan.begin == wvif->scan.curr && + wvif->scan.begin != wvif->scan.end); int i; down(&wvif->scan.lock); mutex_lock(&wvif->wdev->conf_mutex); + if (first_run) { + if (wvif->state == WFX_STATE_STA && + !(wvif->powersave_mode.pm_mode.enter_psm)) { + struct hif_req_set_pm_mode pm = wvif->powersave_mode; + + pm.pm_mode.enter_psm = 1; + wfx_set_pm(wvif, &pm); + } + } + if (!wvif->scan.req || wvif->scan.curr == wvif->scan.end) { if (wvif->scan.output_power != wvif->wdev->output_power) hif_set_output_power(wvif, wvif->wdev->output_power * 10); @@ -147,10 +178,14 @@ void wfx_scan_work(struct work_struct *work) dev_dbg(wvif->wdev->dev, "scan canceled\n"); wvif->scan.req = NULL; + wfx_scan_restart_delayed(wvif); wfx_tx_unlock(wvif->wdev); mutex_unlock(&wvif->wdev->conf_mutex); __ieee80211_scan_completed_compat(wvif->wdev->hw, wvif->scan.status ? 1 : 0); up(&wvif->scan.lock); + if (wvif->state == WFX_STATE_STA && + !(wvif->powersave_mode.pm_mode.enter_psm)) + wfx_set_pm(wvif, &wvif->powersave_mode); return; } first = *wvif->scan.curr; @@ -179,6 +214,11 @@ void wfx_scan_work(struct work_struct *work) scan.ssids = &wvif->scan.ssids[0]; scan.scan_req.num_of_channels = it - wvif->scan.curr; scan.scan_req.probe_delay = 100; + // FIXME: Check if FW can do active scan while joined. + if (wvif->state == WFX_STATE_STA) { + scan.scan_req.scan_type.type = 1; + scan.scan_req.scan_flags.fbg = 1; + } scan.ch = kcalloc(scan.scan_req.num_of_channels, sizeof(u8), GFP_KERNEL); diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index ccf45bdb7e42..1f902ee7623f 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -10,11 +10,152 @@ #include "sta.h" #include "wfx.h" +#include "fwio.h" +#include "bh.h" #include "key.h" #include "scan.h" +#include "debug.h" +#include "hif_tx.h" #include "hif_tx_mib.h" #define TXOP_UNIT 32 +#define HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES 2 + +static u32 wfx_rate_mask_to_hw(struct wfx_dev *wdev, u32 rates) +{ + int i; + u32 ret = 0; + // WFx only support 2GHz + struct ieee80211_supported_band *sband = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; + + for (i = 0; i < sband->n_bitrates; i++) { + if (rates & BIT(i)) { + if (i >= sband->n_bitrates) + dev_warn(wdev->dev, "unsupported basic rate\n"); + else + ret |= BIT(sband->bitrates[i].hw_value); + } + } + return ret; +} + +static void __wfx_free_event_queue(struct list_head *list) +{ + struct wfx_hif_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, list, link) { + list_del(&event->link); + kfree(event); + } +} + +static void wfx_free_event_queue(struct wfx_vif *wvif) +{ + LIST_HEAD(list); + + spin_lock(&wvif->event_queue_lock); + list_splice_init(&wvif->event_queue, &list); + spin_unlock(&wvif->event_queue_lock); + + __wfx_free_event_queue(&list); +} + +void wfx_cqm_bssloss_sm(struct wfx_vif *wvif, int init, int good, int bad) +{ + int tx = 0; + + mutex_lock(&wvif->bss_loss_lock); + wvif->delayed_link_loss = 0; + cancel_work_sync(&wvif->bss_params_work); + + /* If we have a pending unjoin */ + if (wvif->delayed_unjoin) + goto end; + + if (init) { + schedule_delayed_work(&wvif->bss_loss_work, HZ); + wvif->bss_loss_state = 0; + + if (!atomic_read(&wvif->wdev->tx_lock)) + tx = 1; + } else if (good) { + cancel_delayed_work_sync(&wvif->bss_loss_work); + wvif->bss_loss_state = 0; + schedule_work(&wvif->bss_params_work); + } else if (bad) { + /* FIXME Should we just keep going until we time out? */ + if (wvif->bss_loss_state < 3) + tx = 1; + } else { + cancel_delayed_work_sync(&wvif->bss_loss_work); + wvif->bss_loss_state = 0; + } + + /* Spit out a NULL packet to our AP if necessary */ + // FIXME: call ieee80211_beacon_loss/ieee80211_connection_loss instead + if (tx) { + struct sk_buff *skb; + + wvif->bss_loss_state++; + +#if (KERNEL_VERSION(4, 14, 16) > LINUX_VERSION_CODE) + skb = ieee80211_nullfunc_get(wvif->wdev->hw, wvif->vif); +#else + skb = ieee80211_nullfunc_get(wvif->wdev->hw, wvif->vif, false); +#endif + if (!skb) + goto end; + memset(IEEE80211_SKB_CB(skb), 0, sizeof(*IEEE80211_SKB_CB(skb))); + IEEE80211_SKB_CB(skb)->control.vif = wvif->vif; + IEEE80211_SKB_CB(skb)->driver_rates[0].idx = 0; + IEEE80211_SKB_CB(skb)->driver_rates[0].count = 1; + IEEE80211_SKB_CB(skb)->driver_rates[1].idx = -1; + wfx_tx(wvif->wdev->hw, NULL, skb); + } +end: + mutex_unlock(&wvif->bss_loss_lock); +} + +static int wfx_set_uapsd_param(struct wfx_vif *wvif, + const struct wfx_edca_params *arg) +{ + int ret; + + /* Here's the mapping AC [queue, bit] + * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] + */ + + if (arg->uapsd_enable[IEEE80211_AC_VO]) + wvif->uapsd_info.trig_voice = 1; + else + wvif->uapsd_info.trig_voice = 0; + + if (arg->uapsd_enable[IEEE80211_AC_VI]) + wvif->uapsd_info.trig_video = 1; + else + wvif->uapsd_info.trig_video = 0; + + if (arg->uapsd_enable[IEEE80211_AC_BE]) + wvif->uapsd_info.trig_be = 1; + else + wvif->uapsd_info.trig_be = 0; + + if (arg->uapsd_enable[IEEE80211_AC_BK]) + wvif->uapsd_info.trig_bckgrnd = 1; + else + wvif->uapsd_info.trig_bckgrnd = 0; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 + */ + wvif->uapsd_info.min_auto_trigger_interval = 0; + wvif->uapsd_info.max_auto_trigger_interval = 0; + wvif->uapsd_info.auto_trigger_step = 0; + + ret = hif_set_uapsd_info(wvif, &wvif->uapsd_info); + return ret; +} int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable) { @@ -23,6 +164,1053 @@ int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable) wvif->fwd_probe_req); } +static int wfx_set_mcast_filter(struct wfx_vif *wvif, + struct wfx_grp_addr_table *fp) +{ + int i, ret; + struct hif_mib_config_data_filter config = { }; + struct hif_mib_set_data_filtering filter_data = { }; + struct hif_mib_mac_addr_data_frame_condition filter_addr_val = { }; + struct hif_mib_uc_mc_bc_data_frame_condition filter_addr_type = { }; + + // Temporary workaround for filters + return hif_set_data_filtering(wvif, &filter_data); + + if (!fp->enable) { + filter_data.enable = 0; + return hif_set_data_filtering(wvif, &filter_data); + } + + // A1 Address match on list + for (i = 0; i < fp->num_addresses; i++) { + filter_addr_val.condition_idx = i; + filter_addr_val.address_type = HIF_MAC_ADDR_A1; + ether_addr_copy(filter_addr_val.mac_address, fp->address_list[i]); + ret = hif_set_mac_addr_condition(wvif, &filter_addr_val); + if (ret) + return ret; + config.mac_cond |= 1 << i; + } + + // Accept unicast and broadcast + filter_addr_type.condition_idx = 0; + filter_addr_type.param.bits.type_unicast = 1; + filter_addr_type.param.bits.type_broadcast = 1; + ret = hif_set_uc_mc_bc_condition(wvif, &filter_addr_type); + if (ret) + return ret; + + config.uc_mc_bc_cond = 1; + config.filter_idx = 0; // TODO #define MULTICAST_FILTERING 0 + config.enable = 1; + ret = hif_set_config_data_filter(wvif, &config); + if (ret) + return ret; + + // discard all data frames except match filter + filter_data.enable = 1; + filter_data.default_filter = 1; // discard all + ret = hif_set_data_filtering(wvif, &filter_data); + + return ret; +} + +void wfx_update_filtering(struct wfx_vif *wvif) +{ + int ret; + bool is_sta = wvif->vif && NL80211_IFTYPE_STATION == wvif->vif->type; + bool filter_bssid = wvif->filter_bssid; + bool fwd_probe_req = wvif->fwd_probe_req; + struct hif_mib_bcn_filter_enable bf_ctrl; + struct hif_mib_bcn_filter_table *bf_tbl; + struct hif_ie_table_entry ie_tbl[] = { + { + .ie_id = WLAN_EID_VENDOR_SPECIFIC, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + .oui = { 0x50, 0x6F, 0x9A}, + }, { + .ie_id = WLAN_EID_HT_OPERATION, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + }, { + .ie_id = WLAN_EID_ERP_INFO, + .has_changed = 1, + .no_longer = 1, + .has_appeared = 1, + } + }; + + if (wvif->state == WFX_STATE_PASSIVE) + return; + + bf_tbl = kmalloc(sizeof(struct hif_mib_bcn_filter_table) + sizeof(ie_tbl), GFP_KERNEL); + memcpy(bf_tbl->ie_table, ie_tbl, sizeof(ie_tbl)); + if (wvif->disable_beacon_filter) { + bf_ctrl.enable = 0; + bf_ctrl.bcn_count = 1; + bf_tbl->num_of_info_elmts = 0; + } else if (!is_sta) { + bf_ctrl.enable = HIF_BEACON_FILTER_ENABLE | HIF_BEACON_FILTER_AUTO_ERP; + bf_ctrl.bcn_count = 0; + bf_tbl->num_of_info_elmts = 2; + } else { + bf_ctrl.enable = HIF_BEACON_FILTER_ENABLE; + bf_ctrl.bcn_count = 0; + bf_tbl->num_of_info_elmts = 3; + } + + ret = hif_set_rx_filter(wvif, filter_bssid, fwd_probe_req); + if (!ret) + ret = hif_set_beacon_filter_table(wvif, bf_tbl); + if (!ret) + ret = hif_beacon_filter_control(wvif, bf_ctrl.enable, bf_ctrl.bcn_count); + if (!ret) + ret = wfx_set_mcast_filter(wvif, &wvif->mcast_filter); + if (ret) + dev_err(wvif->wdev->dev, "update filtering failed: %d\n", ret); + kfree(bf_tbl); +} + +void wfx_update_filtering_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, update_filtering_work); + + wfx_update_filtering(wvif); +} + +u64 wfx_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) +{ + int i; + struct netdev_hw_addr *ha; + struct wfx_vif *wvif = NULL; + struct wfx_dev *wdev = hw->priv; + int count = netdev_hw_addr_list_count(mc_list); + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + memset(&wvif->mcast_filter, 0x00, sizeof(wvif->mcast_filter)); + if (!count || count > ARRAY_SIZE(wvif->mcast_filter.address_list)) + continue; + + i = 0; + netdev_hw_addr_list_for_each(ha, mc_list) { + ether_addr_copy(wvif->mcast_filter.address_list[i], ha->addr); + i++; + } + wvif->mcast_filter.enable = 1; + wvif->mcast_filter.num_addresses = count; + } + + return 0; +} + +void wfx_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 unused) +{ + struct wfx_vif *wvif = NULL; + struct wfx_dev *wdev = hw->priv; + + *total_flags &= FIF_OTHER_BSS | FIF_FCSFAIL | FIF_PROBE_REQ; + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + down(&wvif->scan.lock); + wvif->filter_bssid = (*total_flags & (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 0 : 1; + wvif->disable_beacon_filter = !(*total_flags & FIF_PROBE_REQ); + wfx_fwd_probe_req(wvif, true); + wfx_update_filtering(wvif); + up(&wvif->scan.lock); + } +} + +int wfx_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + u16 old_uapsd_flags, new_uapsd_flags; + struct hif_req_edca_queue_params *edca; + + mutex_lock(&wdev->conf_mutex); + + if (queue < hw->queues) { + old_uapsd_flags = *((u16 *) &wvif->uapsd_info); + edca = &wvif->edca.params[queue]; + + wvif->edca.uapsd_enable[queue] = params->uapsd; + edca->aifsn = params->aifs; + edca->cw_min = params->cw_min; + edca->cw_max = params->cw_max; + edca->tx_op_limit = params->txop * TXOP_UNIT; + edca->allowed_medium_time = 0; + ret = hif_set_edca_queue_params(wvif, edca); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (wvif->vif->type == NL80211_IFTYPE_STATION) { + ret = wfx_set_uapsd_param(wvif, &wvif->edca); + new_uapsd_flags = *((u16 *) &wvif->uapsd_info); + if (!ret && wvif->setbssparams_done && + wvif->state == WFX_STATE_STA && + old_uapsd_flags != new_uapsd_flags) + ret = wfx_set_pm(wvif, &wvif->powersave_mode); + } + } else { + ret = -EINVAL; + } + +out: + mutex_unlock(&wdev->conf_mutex); + return ret; +} + +int wfx_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg) +{ + struct hif_req_set_pm_mode pm = *arg; + u16 uapsd_flags; + int ret; + + if (wvif->state != WFX_STATE_STA || !wvif->bss_params.aid) + return 0; + + memcpy(&uapsd_flags, &wvif->uapsd_info, sizeof(uapsd_flags)); + + if (uapsd_flags != 0) + pm.pm_mode.fast_psm = 0; + + // Kernel disable PowerSave when multiple vifs are in use. In contrary, + // it is absolutly necessary to enable PowerSave for WF200 + if (wvif_count(wvif->wdev) > 1) { + pm.pm_mode.enter_psm = 1; + pm.pm_mode.fast_psm = 0; + } + + if (!wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300))) + dev_warn(wvif->wdev->dev, "timeout while waiting of set_pm_mode_complete\n"); + ret = hif_set_pm(wvif, &pm); + // FIXME: why ? + if (-ETIMEDOUT == wvif->scan.status) + wvif->scan.status = 1; + return ret; +} + +int wfx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = NULL; + + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) + hif_rts_threshold(wvif, value); + return 0; +} + +/* If successful, LOCKS the TX queue! */ +static int __wfx_flush(struct wfx_dev *wdev, bool drop) +{ + int ret; + + for (;;) { + if (drop) { + wfx_tx_queues_clear(wdev); + } else { + ret = wait_event_timeout( + wdev->tx_queue_stats.wait_link_id_empty, + wfx_tx_queues_is_empty(wdev), + 2 * HZ); + } + + if (!drop && ret <= 0) { + ret = -ETIMEDOUT; + break; + } + ret = 0; + + wfx_tx_lock_flush(wdev); + if (!wfx_tx_queues_is_empty(wdev)) { + /* Highly unlikely: WSM requeued frames. */ + wfx_tx_unlock(wdev); + continue; + } + break; + } + return ret; +} + +void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif; + + if (vif) { + wvif = (struct wfx_vif *) vif->drv_priv; + if (wvif->vif->type == NL80211_IFTYPE_MONITOR) + drop = true; + if (wvif->vif->type == NL80211_IFTYPE_AP && !wvif->enable_beacon) + drop = true; + } + + // FIXME: only flush requested vif + if (!__wfx_flush(wdev, drop)) + wfx_tx_unlock(wdev); +} + +/* WSM callbacks */ + +static void wfx_event_report_rssi(struct wfx_vif *wvif, uint8_t raw_rcpi_rssi) +{ + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpi_rssi; + int cqm_evt; + + rcpi_rssi = raw_rcpi_rssi / 2 - 110; + if (rcpi_rssi <= wvif->cqm_rssi_thold) + cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else + cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; +#if (KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE) + ieee80211_cqm_rssi_notify(wvif->vif, cqm_evt, GFP_KERNEL); +#else + ieee80211_cqm_rssi_notify(wvif->vif, cqm_evt, rcpi_rssi, GFP_KERNEL); +#endif +} + +void wfx_event_handler_work(struct work_struct *work) +{ + struct wfx_vif *wvif = + container_of(work, struct wfx_vif, event_handler_work); + struct wfx_hif_event *event; + + LIST_HEAD(list); + + spin_lock(&wvif->event_queue_lock); + list_splice_init(&wvif->event_queue, &list); + spin_unlock(&wvif->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.event_id) { + case HIF_EVENT_IND_BSSLOST: + cancel_work_sync(&wvif->unjoin_work); + if (!down_trylock(&wvif->scan.lock)) { + wfx_cqm_bssloss_sm(wvif, 1, 0, 0); + up(&wvif->scan.lock); + } else { + /* Scan is in progress. Delay reporting. + * Scan complete will trigger bss_loss_work + */ + wvif->delayed_link_loss = 1; + /* Also start a watchdog. */ + schedule_delayed_work(&wvif->bss_loss_work, 5 * HZ); + } + break; + case HIF_EVENT_IND_BSSREGAINED: + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); + break; + case HIF_EVENT_IND_RCPI_RSSI: + wfx_event_report_rssi(wvif, event->evt.event_data.rcpi_rssi); + break; + case HIF_EVENT_IND_PS_MODE_ERROR: + dev_warn(wvif->wdev->dev, "error while processing power save request\n"); + break; + default: + dev_warn(wvif->wdev->dev, "unhandled event indication: %.2x\n", event->evt.event_id); + break; + } + } + __wfx_free_event_queue(&list); +} + +void wfx_bss_loss_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, bss_loss_work.work); + + ieee80211_connection_loss(wvif->vif); +} + +void wfx_bss_params_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, bss_params_work); + + mutex_lock(&wvif->wdev->conf_mutex); + wvif->bss_params.bss_flags.lost_count_only = 1; + hif_set_bss_params(wvif, &wvif->bss_params); + wvif->bss_params.bss_flags.lost_count_only = 0; + mutex_unlock(&wvif->wdev->conf_mutex); +} + +void wfx_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_beacon_wakeup_period_work); + + hif_set_beacon_wakeup_period(wvif, wvif->dtim_period, wvif->dtim_period); +} + +static void wfx_do_unjoin(struct wfx_vif *wvif) +{ + mutex_lock(&wvif->wdev->conf_mutex); + + if (atomic_read(&wvif->scan.in_progress)) { + if (wvif->delayed_unjoin) + dev_dbg(wvif->wdev->dev, "delayed unjoin is already scheduled\n"); + else + wvif->delayed_unjoin = true; + goto done; + } + + wvif->delayed_link_loss = false; + + if (!wvif->state) + goto done; + + if (wvif->state == WFX_STATE_AP) + goto done; + + cancel_work_sync(&wvif->update_filtering_work); + cancel_work_sync(&wvif->set_beacon_wakeup_period_work); + wvif->state = WFX_STATE_PASSIVE; + + /* Unjoin is a reset. */ + wfx_tx_flush(wvif->wdev); + hif_keep_alive_period(wvif, 0); + hif_reset(wvif, false); + hif_set_output_power(wvif, wvif->wdev->output_power * 10); + wvif->dtim_period = 0; + hif_set_macaddr(wvif, wvif->vif->addr); + wfx_free_event_queue(wvif); + cancel_work_sync(&wvif->event_handler_work); + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + + /* Disable Block ACKs */ + hif_set_block_ack_policy(wvif, 0, 0); + + wvif->disable_beacon_filter = false; + wfx_update_filtering(wvif); + memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); + wvif->setbssparams_done = false; + memset(&wvif->ht_info, 0, sizeof(wvif->ht_info)); + +done: + mutex_unlock(&wvif->wdev->conf_mutex); +} + +static void wfx_set_mfp(struct wfx_vif *wvif, struct cfg80211_bss *bss) +{ + const int pairwise_cipher_suite_count_offset = 8 / sizeof(uint16_t); + const int pairwise_cipher_suite_size = 4 / sizeof(uint16_t); + const int akm_suite_size = 4 / sizeof(uint16_t); + const uint16_t *ptr = NULL; + bool mfpc = false; + bool mfpr = false; + + /* 802.11w protected mgmt frames */ + + /* retrieve MFPC and MFPR flags from beacon or PBRSP */ + + rcu_read_lock(); + if (bss) + ptr = (const uint16_t *) ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + if (ptr) { + ptr += pairwise_cipher_suite_count_offset; + ptr += 1 + pairwise_cipher_suite_size * *ptr; + ptr += 1 + akm_suite_size * *ptr; + mfpr = *ptr & BIT(6); + mfpc = *ptr & BIT(7); + } + rcu_read_unlock(); + + hif_set_mfp(wvif, mfpc, mfpr); +} + +/* MUST be called with tx_lock held! It will be unlocked for us. */ +static void wfx_do_join(struct wfx_vif *wvif) +{ + const u8 *bssid; + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + struct cfg80211_bss *bss = NULL; + struct hif_req_join join = { + .mode = conf->ibss_joined ? HIF_MODE_IBSS : HIF_MODE_BSS, + .preamble_type = conf->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG, + .probe_for_join = 1, + .atim_window = 0, + .basic_rate_set = wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates), + }; + + if (wvif->channel->flags & IEEE80211_CHAN_NO_IR) + join.probe_for_join = 0; + + if (wvif->state) + wfx_do_unjoin(wvif); + + bssid = wvif->vif->bss_conf.bssid; + +#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE) + bss = cfg80211_get_bss(wvif->wdev->hw->wiphy, wvif->channel, bssid, NULL, 0, + 0, 0); +#else + bss = cfg80211_get_bss(wvif->wdev->hw->wiphy, wvif->channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); +#endif + + if (!bss && !conf->ibss_joined) { + wfx_tx_unlock(wvif->wdev); + return; + } + + mutex_lock(&wvif->wdev->conf_mutex); + + /* Under the conf lock: check scan status and + * bail out if it is in progress. + */ + if (atomic_read(&wvif->scan.in_progress)) { + wfx_tx_unlock(wvif->wdev); + goto done_put; + } + + /* Sanity check basic rates */ + if (!join.basic_rate_set) + join.basic_rate_set = 7; + + /* Sanity check beacon interval */ + if (!wvif->beacon_int) + wvif->beacon_int = 1; + + join.beacon_interval = wvif->beacon_int; + + // DTIM period will be set on first Beacon + wvif->dtim_period = 0; + + join.channel_number = wvif->channel->hw_value; + memcpy(join.bssid, bssid, sizeof(join.bssid)); + + if (!conf->ibss_joined) { + const u8 *ssidie; + + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssidie) { + join.ssid_length = ssidie[1]; + memcpy(join.ssid, &ssidie[2], join.ssid_length); + } + rcu_read_unlock(); + } + + wfx_tx_flush(wvif->wdev); + + if (wvif_count(wvif->wdev) <= 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + + wfx_set_mfp(wvif, bss); + + /* Perform actual join */ + wvif->wdev->tx_burst_idx = -1; + if (hif_join(wvif, &join)) { + ieee80211_connection_loss(wvif->vif); + wvif->join_complete_status = -1; + /* Tx lock still held, unjoin will clear it. */ + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wvif->wdev); + } else { + wvif->join_complete_status = 0; + if (wvif->vif->type == NL80211_IFTYPE_ADHOC) + wvif->state = WFX_STATE_IBSS; + else + wvif->state = WFX_STATE_PRE_STA; + wfx_tx_unlock(wvif->wdev); + + /* Upload keys */ + wfx_upload_keys(wvif); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one + */ + wvif->disable_beacon_filter = true; + } + wfx_update_filtering(wvif); + +done_put: + mutex_unlock(&wvif->wdev->conf_mutex); + if (bss) + cfg80211_put_bss(wvif->wdev->hw->wiphy, bss); +} + +void wfx_unjoin_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, unjoin_work); + + wfx_do_unjoin(wvif); + wfx_tx_unlock(wvif->wdev); +} + +int wfx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_link_entry *entry; + struct sk_buff *skb; + + if (wvif->vif->type != NL80211_IFTYPE_AP) + return 0; + + sta_priv->vif_id = wvif->id; + sta_priv->link_id = wfx_find_link_id(wvif, sta->addr); + if (!sta_priv->link_id) { + dev_warn(wdev->dev, "mo more link-id available\n"); + return -ENOENT; + } + + entry = &wvif->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&wvif->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + wvif->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = WFX_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(wdev->hw, skb); + spin_unlock_bh(&wvif->ps_state_lock); + return 0; +} + +int wfx_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_link_entry *entry; + + if (wvif->vif->type != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &wvif->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&wvif->ps_state_lock); + entry->status = WFX_LINK_RESERVE; + entry->timestamp = jiffies; + wfx_tx_lock(wdev); + if (!schedule_work(&wvif->link_id_work)) + wfx_tx_unlock(wdev); + spin_unlock_bh(&wvif->ps_state_lock); + flush_work(&wvif->link_id_work); + return 0; +} + +void wfx_set_cts_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_cts_work); + u8 erp_ie[3] = { WLAN_EID_ERP_INFO, 1, 0 }; + struct hif_ie_flags target_frame = { + .beacon = 1, + }; + + mutex_lock(&wvif->wdev->conf_mutex); + erp_ie[2] = wvif->erp_info; + mutex_unlock(&wvif->wdev->conf_mutex); + + hif_erp_use_protection(wvif, erp_ie[2] & WLAN_ERP_USE_PROTECTION); + + if (wvif->vif->type != NL80211_IFTYPE_STATION) + hif_update_ie(wvif, &target_frame, erp_ie, sizeof(erp_ie)); +} + +static int wfx_start_ap(struct wfx_vif *wvif) +{ + int ret; + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + struct hif_req_start start = { + .channel_number = wvif->channel->hw_value, + .beacon_interval = conf->beacon_int, + .dtim_period = conf->dtim_period, + .preamble_type = conf->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG, + .basic_rate_set = wfx_rate_mask_to_hw(wvif->wdev, conf->basic_rates), + }; + + memset(start.ssid, 0, sizeof(start.ssid)); + if (!conf->hidden_ssid) { + start.ssid_length = conf->ssid_len; + memcpy(start.ssid, conf->ssid, start.ssid_length); + } + + wvif->beacon_int = conf->beacon_int; + wvif->dtim_period = conf->dtim_period; + + memset(&wvif->link_id_db, 0, sizeof(wvif->link_id_db)); + + wvif->wdev->tx_burst_idx = -1; + ret = hif_start(wvif, &start); + if (!ret) + ret = wfx_upload_keys(wvif); + if (!ret) { + if (wvif_count(wvif->wdev) <= 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + wvif->state = WFX_STATE_AP; + wfx_update_filtering(wvif); + } + return ret; +} + +static int wfx_update_beaconing(struct wfx_vif *wvif) +{ + struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; + + if (wvif->vif->type == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (wvif->state != WFX_STATE_AP || + wvif->beacon_int != conf->beacon_int) { + wfx_tx_lock_flush(wvif->wdev); + if (wvif->state != WFX_STATE_PASSIVE) + hif_reset(wvif, false); + wvif->state = WFX_STATE_PASSIVE; + wfx_start_ap(wvif); + wfx_tx_unlock(wvif->wdev); + } else { + } + } + return 0; +} + +static int wfx_upload_beacon(struct wfx_vif *wvif) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct ieee80211_mgmt *mgmt; + struct hif_mib_template_frame *p; + + if (wvif->vif->type == NL80211_IFTYPE_STATION || + wvif->vif->type == NL80211_IFTYPE_MONITOR || + wvif->vif->type == NL80211_IFTYPE_UNSPECIFIED) + goto done; + + skb = ieee80211_beacon_get(wvif->wdev->hw, wvif->vif); + + if (!skb) + return -ENOMEM; + + p = (struct hif_mib_template_frame *) skb_push(skb, 4); + p->frame_type = HIF_TMPLT_BCN; + p->init_rate = API_RATE_INDEX_B_1MBPS; /* 1Mbps DSSS */ + p->frame_length = cpu_to_le16(skb->len - 4); + + ret = hif_set_template_frame(wvif, p); + + skb_pull(skb, 4); + + if (ret) + goto done; + /* TODO: Distill probe resp; remove TIM and any other beacon-specific + * IEs + */ + mgmt = (void *)skb->data; + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); + + p->frame_type = HIF_TMPLT_PRBRES; + + ret = hif_set_template_frame(wvif, p); + wfx_fwd_probe_req(wvif, false); + +done: + if (!skb) + dev_kfree_skb(skb); + return ret; +} + +static int wfx_is_ht(const struct wfx_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static int wfx_ht_greenfield(const struct wfx_ht_info *ht_info) +{ + return wfx_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static int wfx_ht_ampdu_density(const struct wfx_ht_info *ht_info) +{ + if (!wfx_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +static void wfx_join_finalize(struct wfx_vif *wvif, struct ieee80211_bss_conf *info) +{ + struct ieee80211_sta *sta = NULL; + struct hif_mib_set_association_mode association_mode = { }; + + if (info->dtim_period) + wvif->dtim_period = info->dtim_period; + wvif->beacon_int = info->beacon_int; + + rcu_read_lock(); + if (info->bssid && !info->ibss_joined) + sta = ieee80211_find_sta(wvif->vif, info->bssid); + if (sta) { + wvif->ht_info.ht_cap = sta->ht_cap; + wvif->bss_params.operational_rate_set = + wfx_rate_mask_to_hw(wvif->wdev, sta->supp_rates[wvif->channel->band]); + wvif->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&wvif->ht_info, 0, sizeof(wvif->ht_info)); + wvif->bss_params.operational_rate_set = -1; + } + rcu_read_unlock(); + + /* Non Greenfield stations present */ + if (wvif->ht_info.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + hif_dual_cts_protection(wvif, true); + else + hif_dual_cts_protection(wvif, false); + + association_mode.preambtype_use = 1; + association_mode.mode = 1; + association_mode.rateset = 1; + association_mode.spacing = 1; + association_mode.preamble_type = info->use_short_preamble ? HIF_PREAMBLE_SHORT : HIF_PREAMBLE_LONG; + association_mode.basic_rate_set = cpu_to_le32(wfx_rate_mask_to_hw(wvif->wdev, info->basic_rates)); + association_mode.mixed_or_greenfield_type = wfx_ht_greenfield(&wvif->ht_info); + association_mode.mpdu_start_spacing = wfx_ht_ampdu_density(&wvif->ht_info); + + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); + + wvif->bss_params.beacon_lost_count = 20; + wvif->bss_params.aid = info->aid; + + if (wvif->dtim_period < 1) + wvif->dtim_period = 1; + + hif_set_association_mode(wvif, &association_mode); + + if (!info->ibss_joined) { + hif_keep_alive_period(wvif, 30 /* sec */); + hif_set_bss_params(wvif, &wvif->bss_params); + wvif->setbssparams_done = true; + wfx_set_beacon_wakeup_period_work(&wvif->set_beacon_wakeup_period_work); + wfx_set_pm(wvif, &wvif->powersave_mode); + } +} + +void wfx_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + bool do_join = false; + int i; + int nb_arp_addr; + + mutex_lock(&wdev->conf_mutex); + + /* TODO: BSS_CHANGED_QOS */ + if (changed & BSS_CHANGED_ARP_FILTER) { + struct hif_mib_arp_ip_addr_table filter = { }; + + nb_arp_addr = info->arp_addr_cnt; + if (nb_arp_addr <= 0 || nb_arp_addr > HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES) + nb_arp_addr = 0; + + for (i = 0; i < HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES; i++) { + filter.condition_idx = i; + if (i < nb_arp_addr) { + // Caution: type of arp_addr_list[i] is __be32 + memcpy(filter.ipv4_address, &info->arp_addr_list[i], sizeof(filter.ipv4_address)); + filter.arp_enable = HIF_ARP_NS_FILTERING_ENABLE; + } else { + filter.arp_enable = HIF_ARP_NS_FILTERING_DISABLE; + } + hif_set_arp_ipv4_filter(wvif, &filter); + } + } + + if (changed & + (BSS_CHANGED_BEACON | BSS_CHANGED_AP_PROBE_RESP | + BSS_CHANGED_BSSID | BSS_CHANGED_SSID | BSS_CHANGED_IBSS)) { + wvif->beacon_int = info->beacon_int; + wfx_update_beaconing(wvif); + wfx_upload_beacon(wvif); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED && wvif->state != WFX_STATE_IBSS) { + if (wvif->enable_beacon != info->enable_beacon) { + hif_beacon_transmit(wvif, info->enable_beacon); + wvif->enable_beacon = info->enable_beacon; + } + } + + /* assoc/disassoc, or maybe AID changed */ + if (changed & BSS_CHANGED_ASSOC) { + wfx_tx_lock_flush(wdev); + wvif->wep_default_key_id = -1; + wfx_tx_unlock(wdev); + } + + if (changed & BSS_CHANGED_ASSOC && !info->assoc && + (wvif->state == WFX_STATE_STA || wvif->state == WFX_STATE_IBSS)) { + /* Shedule unjoin work */ + wfx_tx_lock(wdev); + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wdev); + } else { + if (changed & BSS_CHANGED_BEACON_INT) { + if (info->ibss_joined) + do_join = true; + else if (wvif->state == WFX_STATE_AP) + wfx_update_beaconing(wvif); + } + + if (changed & BSS_CHANGED_BSSID) + do_join = true; + + if (changed & + (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID | + BSS_CHANGED_IBSS | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_HT)) { + if (info->assoc) { + if (wvif->state < WFX_STATE_PRE_STA) { + ieee80211_connection_loss(vif); + mutex_unlock(&wdev->conf_mutex); + return; + } else if (wvif->state == WFX_STATE_PRE_STA) { + wvif->state = WFX_STATE_STA; + } + } else { + do_join = true; + } + + if (info->assoc || info->ibss_joined) + wfx_join_finalize(wvif, info); + else + memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); + } + } + + /* ERP Protection */ + if (changed & (BSS_CHANGED_ASSOC | + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE)) { + u32 prev_erp_info = wvif->erp_info; + + if (info->use_cts_prot) + wvif->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + wvif->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (info->use_short_preamble) + wvif->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + wvif->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + if (prev_erp_info != wvif->erp_info) + schedule_work(&wvif->set_cts_work); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) + hif_slot_time(wvif, info->use_short_slot ? 9 : 20); + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct hif_mib_rcpi_rssi_threshold th = { + .rolling_average_count = 8, + .detection = 1, + }; + + wvif->cqm_rssi_thold = info->cqm_rssi_thold; + + if (!info->cqm_rssi_thold && !info->cqm_rssi_hyst) { + th.upperthresh = 1; + th.lowerthresh = 1; + } else { + /* FIXME It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more reliable and stable. + */ + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + th.upper_threshold = info->cqm_rssi_thold + info->cqm_rssi_hyst; + th.upper_threshold = (th.upper_threshold + 110) * 2; + th.lower_threshold = info->cqm_rssi_thold; + th.lower_threshold = (th.lower_threshold + 110) * 2; + } + hif_set_rcpi_rssi_threshold(wvif, &th); + } + + if (changed & BSS_CHANGED_TXPOWER && info->txpower != wdev->output_power) { + wdev->output_power = info->txpower; + hif_set_output_power(wvif, wdev->output_power * 10); + } + mutex_unlock(&wdev->conf_mutex); + + if (do_join) { + wfx_tx_lock_flush(wdev); + wfx_do_join(wvif); /* Will unlock it for us */ + } +} + +static void wfx_ps_notify(struct wfx_vif *wvif, enum sta_notify_cmd notify_cmd, + int link_id) +{ + u32 bit, prev; + + spin_lock_bh(&wvif->ps_state_lock); + /* Zero link id means "for all link IDs" */ + if (link_id) { + bit = BIT(link_id); + } else if (notify_cmd != STA_NOTIFY_AWAKE) { + dev_warn(wvif->wdev->dev, "unsupported notify command\n"); + bit = 0; + } else { + bit = wvif->link_id_map; + } + prev = wvif->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (wvif->mcast_buffered && !wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_start_work); + wvif->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + wvif->sta_asleep_mask &= ~bit; + wvif->pspoll_mask &= ~bit; + if (link_id && !wvif->sta_asleep_mask) + schedule_work(&wvif->mcast_stop_work); + wfx_bh_request_tx(wvif->wdev); + } + break; + } + spin_unlock_bh(&wvif->ps_state_lock); +} + +void wfx_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; + + wfx_ps_notify(wvif, notify_cmd, sta_priv->link_id); +} + static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) { struct sk_buff *skb; @@ -34,8 +1222,11 @@ static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) skb = ieee80211_beacon_get_tim(wvif->wdev->hw, wvif->vif, &tim_offset, &tim_length); - if (!skb) + if (!skb) { + if (!__wfx_flush(wvif->wdev, true)) + wfx_tx_unlock(wvif->wdev); return -ENOENT; + } tim_ptr = skb->data + tim_offset; if (tim_offset && tim_length >= 6) { @@ -57,16 +1248,34 @@ static int wfx_set_tim_impl(struct wfx_vif *wvif, bool aid0_bit_set) return 0; } +void wfx_set_tim_work(struct work_struct *work) +{ + struct wfx_vif *wvif = container_of(work, struct wfx_vif, set_tim_work); + + wfx_set_tim_impl(wvif, wvif->aid0_bit_set); +} + +int wfx_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) +{ + struct wfx_dev *wdev = hw->priv; + struct wfx_sta_priv *sta_dev = (struct wfx_sta_priv *) &sta->drv_priv; + struct wfx_vif *wvif = wdev_to_wvif(wdev, sta_dev->vif_id); + + schedule_work(&wvif->set_tim_work); + return 0; +} + static void wfx_mcast_start_work(struct work_struct *work) { struct wfx_vif *wvif = container_of(work, struct wfx_vif, mcast_start_work); + long tmo = wvif->dtim_period * TU_TO_JIFFIES(wvif->beacon_int + 20); cancel_work_sync(&wvif->mcast_stop_work); if (!wvif->aid0_bit_set) { wfx_tx_lock_flush(wvif->wdev); wfx_set_tim_impl(wvif, true); wvif->aid0_bit_set = true; - mod_timer(&wvif->mcast_timeout, TU_TO_JIFFIES(1000)); + mod_timer(&wvif->mcast_timeout, jiffies + tmo); wfx_tx_unlock(wvif->wdev); } } @@ -101,6 +1310,148 @@ static void wfx_mcast_timeout(struct timer_list *t) spin_unlock_bh(&wvif->ps_state_lock); } +#if (KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE) +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, + u16 *ssn, u8 buf_size) +#else +#if (KERNEL_VERSION(4, 4, 69) > LINUX_VERSION_CODE) +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +#else +int wfx_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +#endif +#endif +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. + */ + + /* Note that we still need this function stubbed. */ + + return -ENOTSUPP; +} + +void wfx_suspend_resume(struct wfx_vif *wvif, + struct hif_ind_suspend_resume_tx *arg) +{ + if (arg->suspend_resume_flags.bc_mc_only) { + bool cancel_tmo = false; + + spin_lock_bh(&wvif->ps_state_lock); + if (!arg->suspend_resume_flags.resume) + wvif->mcast_tx = false; + else + wvif->mcast_tx = wvif->aid0_bit_set && wvif->mcast_buffered; + if (wvif->mcast_tx) { + cancel_tmo = true; + wfx_bh_request_tx(wvif->wdev); + } + spin_unlock_bh(&wvif->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&wvif->mcast_timeout); + } else if (arg->suspend_resume_flags.resume) { + // FIXME: should change each station status independently + wfx_ps_notify(wvif, STA_NOTIFY_AWAKE, 0); + wfx_bh_request_tx(wvif->wdev); + } else { + // FIXME: should change each station status independently + wfx_ps_notify(wvif, STA_NOTIFY_SLEEP, 0); + } +} + +int wfx_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + return 0; +} + +void wfx_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ +} + +void wfx_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed) +{ +} + +int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct ieee80211_channel *ch = conf->def.chan; + + WARN(wvif->channel, "channel overwrite"); + wvif->channel = ch; + wvif->ht_info.channel_type = cfg80211_get_chandef_type(&conf->def); + + return 0; +} + +void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + struct ieee80211_channel *ch = conf->def.chan; + + WARN(wvif->channel != ch, "channel mismatch"); + wvif->channel = NULL; +} + +int wfx_config(struct ieee80211_hw *hw, u32 changed) +{ + int ret = 0; + struct wfx_dev *wdev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + struct wfx_vif *wvif; + + // FIXME: Interface id should not been hardcoded + wvif = wdev_to_wvif(wdev, 0); + if (!wvif) { + WARN(1, "interface 0 does not exist anymore"); + return 0; + } + + down(&wvif->scan.lock); + mutex_lock(&wdev->conf_mutex); + if (changed & IEEE80211_CONF_CHANGE_POWER) { + wdev->output_power = conf->power_level; + hif_set_output_power(wvif, wdev->output_power * 10); + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + memset(&wvif->powersave_mode, 0, sizeof(wvif->powersave_mode)); + if (conf->flags & IEEE80211_CONF_PS) { + wvif->powersave_mode.pm_mode.enter_psm = 1; + if (conf->dynamic_ps_timeout > 0) { + wvif->powersave_mode.pm_mode.fast_psm = 1; + // Firmware does not support more than 128ms + wvif->powersave_mode.fast_psm_idle_period = + min(conf->dynamic_ps_timeout * 2, 255); + } + } + if (wvif->state == WFX_STATE_STA && wvif->bss_params.aid) + wfx_set_pm(wvif, &wvif->powersave_mode); + } + wvif = wdev_to_wvif(wdev, 0); + } + + mutex_unlock(&wdev->conf_mutex); + up(&wvif->scan.lock); + return ret; +} + int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int i; @@ -144,8 +1495,24 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) default_edca_params[IEEE80211_AC_BK].queue_id = HIF_QUEUE_ID_BESTEFFORT; } + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | +#if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE) + IEEE80211_VIF_SUPPORTS_UAPSD | +#endif + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + mutex_lock(&wdev->conf_mutex); + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + break; + default: + mutex_unlock(&wdev->conf_mutex); + return -EOPNOTSUPP; + } + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { if (!wdev->vif[i]) { wdev->vif[i] = vif; @@ -157,6 +1524,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_unlock(&wdev->conf_mutex); return -EOPNOTSUPP; } + // FIXME: prefer use of container_of() to get vif wvif->vif = vif; wvif->wdev = wdev; @@ -164,6 +1532,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) INIT_DELAYED_WORK(&wvif->link_id_gc_work, wfx_link_id_gc_work); spin_lock_init(&wvif->ps_state_lock); + INIT_WORK(&wvif->set_tim_work, wfx_set_tim_work); INIT_WORK(&wvif->mcast_start_work, wfx_mcast_start_work); INIT_WORK(&wvif->mcast_stop_work, wfx_mcast_stop_work); @@ -173,6 +1542,10 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) timer_setup(&wvif->mcast_timeout, wfx_mcast_timeout, 0); #endif + wvif->setbssparams_done = false; + mutex_init(&wvif->bss_loss_lock); + INIT_DELAYED_WORK(&wvif->bss_loss_work, wfx_bss_loss_work); + wvif->wep_default_key_id = -1; INIT_WORK(&wvif->wep_key_work, wfx_wep_key_work); @@ -180,22 +1553,115 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) INIT_WORK(&wvif->scan.work, wfx_scan_work); INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout); + spin_lock_init(&wvif->event_queue_lock); + INIT_LIST_HEAD(&wvif->event_queue); + INIT_WORK(&wvif->event_handler_work, wfx_event_handler_work); + + init_completion(&wvif->set_pm_mode_complete); + complete(&wvif->set_pm_mode_complete); + INIT_WORK(&wvif->set_beacon_wakeup_period_work, wfx_set_beacon_wakeup_period_work); + INIT_WORK(&wvif->update_filtering_work, wfx_update_filtering_work); + INIT_WORK(&wvif->bss_params_work, wfx_bss_params_work); + INIT_WORK(&wvif->set_cts_work, wfx_set_cts_work); + INIT_WORK(&wvif->unjoin_work, wfx_unjoin_work); + mutex_unlock(&wdev->conf_mutex); + + hif_set_macaddr(wvif, vif->addr); BUG_ON(ARRAY_SIZE(default_edca_params) != ARRAY_SIZE(wvif->edca.params)); - for (i = 0; i < IEEE80211_NUM_ACS; i++) + for (i = 0; i < IEEE80211_NUM_ACS; i++) { memcpy(&wvif->edca.params[i], &default_edca_params[i], sizeof(default_edca_params[i])); + wvif->edca.uapsd_enable[i] = false; + hif_set_edca_queue_params(wvif, &wvif->edca.params[i]); + } + wfx_set_uapsd_param(wvif, &wvif->edca); + tx_policy_init(wvif); + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + // Combo mode does not support Block Acks. We can re-enable them + if (wvif_count(wdev) == 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + else + hif_set_block_ack_policy(wvif, 0x00, 0x00); + // Combo force powersave mode. We can re-enable it now + wfx_set_pm(wvif, &wvif->powersave_mode); + } return 0; } void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct wfx_dev *wdev = hw->priv; struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; + int i; + // If scan is in progress, stop it + while (down_trylock(&wvif->scan.lock)) + schedule(); + up(&wvif->scan.lock); + wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300)); + + mutex_lock(&wdev->conf_mutex); + switch (wvif->state) { + case WFX_STATE_PRE_STA: + case WFX_STATE_STA: + case WFX_STATE_IBSS: + wfx_tx_lock_flush(wdev); + if (!schedule_work(&wvif->unjoin_work)) + wfx_tx_unlock(wdev); + break; + case WFX_STATE_AP: + for (i = 0; wvif->link_id_map; ++i) { + if (wvif->link_id_map & BIT(i)) { + wfx_unmap_link(wvif, i); + wvif->link_id_map &= ~BIT(i); + } + } + memset(wvif->link_id_db, 0, sizeof(wvif->link_id_db)); + wvif->sta_asleep_mask = 0; + wvif->enable_beacon = false; + wvif->mcast_tx = false; + wvif->aid0_bit_set = false; + wvif->mcast_buffered = false; + wvif->pspoll_mask = 0; + /* reset.link_id = 0; */ + hif_reset(wvif, false); + break; + default: + break; + } + + wvif->state = WFX_STATE_PASSIVE; wfx_tx_queues_wait_empty_vif(wvif); + wfx_tx_unlock(wdev); + + /* FIXME: In add to reset MAC address, try to reset interface */ + hif_set_macaddr(wvif, NULL); + + cancel_delayed_work_sync(&wvif->scan.timeout); + + wfx_cqm_bssloss_sm(wvif, 0, 0, 0); + cancel_work_sync(&wvif->unjoin_work); cancel_delayed_work_sync(&wvif->link_id_gc_work); del_timer_sync(&wvif->mcast_timeout); + wfx_free_event_queue(wvif); + + wdev->vif[wvif->id] = NULL; + wvif->vif = NULL; + + mutex_unlock(&wdev->conf_mutex); + wvif = NULL; + while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { + // Combo mode does not support Block Acks. We can re-enable them + if (wvif_count(wdev) == 1) + hif_set_block_ack_policy(wvif, 0xFF, 0xFF); + else + hif_set_block_ack_policy(wvif, 0x00, 0x00); + // Combo force powersave mode. We can re-enable it now + wfx_set_pm(wvif, &wvif->powersave_mode); + } } int wfx_start(struct ieee80211_hw *hw) diff --git a/drivers/staging/wfx/sta.h b/drivers/staging/wfx/sta.h index dd1b6b3fc2f1..c77f58516633 100644 --- a/drivers/staging/wfx/sta.h +++ b/drivers/staging/wfx/sta.h @@ -8,18 +8,45 @@ #ifndef WFX_STA_H #define WFX_STA_H +#include #include #include "hif_api_cmd.h" +struct wfx_dev; struct wfx_vif; +enum wfx_state { + WFX_STATE_PASSIVE = 0, + WFX_STATE_PRE_STA, + WFX_STATE_STA, + WFX_STATE_IBSS, + WFX_STATE_AP, +}; + +struct wfx_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + uint16_t operation_mode; +}; + +struct wfx_hif_event { + struct list_head link; + struct hif_ind_event evt; +}; + struct wfx_edca_params { /* NOTE: index is a linux queue id. */ struct hif_req_edca_queue_params params[IEEE80211_NUM_ACS]; bool uapsd_enable[IEEE80211_NUM_ACS]; }; +struct wfx_grp_addr_table { + bool enable; + int num_addresses; + u8 address_list[8][ETH_ALEN]; +}; + struct wfx_sta_priv { int link_id; int vif_id; @@ -28,9 +55,62 @@ struct wfx_sta_priv { // mac80211 interface int wfx_start(struct ieee80211_hw *hw); void wfx_stop(struct ieee80211_hw *hw); +int wfx_config(struct ieee80211_hw *hw, u32 changed); +int wfx_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +u64 wfx_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); +void wfx_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 unused); + int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int wfx_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +void wfx_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed); +int wfx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int wfx_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void wfx_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta); +int wfx_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); +#if (KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE) +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size); +#else +#if (KERNEL_VERSION(4, 4, 69) > LINUX_VERSION_CODE) +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, + bool amsdu); +#else +int wfx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params); +#endif +#endif +int wfx_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void wfx_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void wfx_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, u32 changed); +int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf); +void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf); + +// WSM Callbacks +void wfx_suspend_resume(struct wfx_vif *wvif, struct hif_ind_suspend_resume_tx *arg); + +// Other Helpers +void wfx_cqm_bssloss_sm(struct wfx_vif *wvif, int init, int good, int bad); +void wfx_update_filtering(struct wfx_vif *wvif); +int wfx_set_pm(struct wfx_vif *wvif, const struct hif_req_set_pm_mode *arg); int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable); #endif /* WFX_STA_H */ diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h index ba5e1a32d869..b353be5f0f7c 100644 --- a/drivers/staging/wfx/wfx.h +++ b/drivers/staging/wfx/wfx.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include "bh.h" @@ -24,6 +26,15 @@ #include "hif_tx.h" #include "hif_api_general.h" +#if (KERNEL_VERSION(4, 2, 0) > LINUX_VERSION_CODE) +static inline void _ieee80211_hw_set(struct ieee80211_hw *hw, + enum ieee80211_hw_flags flg) +{ + hw->flags |= flg; +} +#define ieee80211_hw_set(hw, flg) _ieee80211_hw_set(hw, IEEE80211_HW_##flg) +#endif + #if (KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE) #define nl80211_band ieee80211_band #define NL80211_BAND_2GHZ IEEE80211_BAND_2GHZ @@ -82,8 +93,15 @@ struct wfx_dev { struct wfx_vif { struct wfx_dev *wdev; struct ieee80211_vif *vif; + struct ieee80211_channel *channel; int id; + enum wfx_state state; + int delayed_link_loss; + int bss_loss_state; + u32 bss_loss_confirm_id; + struct mutex bss_loss_lock; + struct delayed_work bss_loss_work; u32 link_id_map; struct wfx_link_entry link_id_db[WFX_MAX_STA_IN_AP_MODE]; @@ -93,6 +111,7 @@ struct wfx_vif { bool aid0_bit_set; bool mcast_tx; bool mcast_buffered; + struct wfx_grp_addr_table mcast_filter; struct timer_list mcast_timeout; struct work_struct mcast_start_work; struct work_struct mcast_stop_work; @@ -107,13 +126,40 @@ struct wfx_vif { u32 sta_asleep_mask; u32 pspoll_mask; spinlock_t ps_state_lock; + struct work_struct set_tim_work; + + int dtim_period; + int beacon_int; + bool enable_beacon; + struct work_struct set_beacon_wakeup_period_work; bool filter_bssid; bool fwd_probe_req; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + u32 erp_info; + int cqm_rssi_thold; + bool setbssparams_done; + struct wfx_ht_info ht_info; struct wfx_edca_params edca; + struct hif_mib_set_uapsd_information uapsd_info; + struct hif_req_set_bss_params bss_params; + struct work_struct bss_params_work; + struct work_struct set_cts_work; + + int join_complete_status; + bool delayed_unjoin; + struct work_struct unjoin_work; struct wfx_scan scan; + + struct hif_req_set_pm_mode powersave_mode; + struct completion set_pm_mode_complete; + + struct list_head event_queue; + spinlock_t event_queue_lock; + struct work_struct event_handler_work; }; static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id) @@ -147,6 +193,20 @@ static inline struct wfx_vif *wvif_iterate(struct wfx_dev *wdev, struct wfx_vif return NULL; } +static inline int wvif_count(struct wfx_dev *wdev) +{ + int i; + int ret = 0; + struct wfx_vif *wvif; + + for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { + wvif = wdev_to_wvif(wdev, i); + if (wvif) + ret++; + } + return ret; +} + static inline void memreverse(uint8_t *src, uint8_t length) { uint8_t *lo = src;