From patchwork Thu Sep 19 14:25: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: 11152701 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 972AD14ED for ; Thu, 19 Sep 2019 14:27:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 560EC21928 for ; Thu, 19 Sep 2019 14:27:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=silabs.onmicrosoft.com header.i=@silabs.onmicrosoft.com header.b="Z5laYT6p" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389707AbfISO1m (ORCPT ); Thu, 19 Sep 2019 10:27:42 -0400 Received: from mail-eopbgr780053.outbound.protection.outlook.com ([40.107.78.53]:38626 "EHLO NAM03-BY2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1732411AbfISOZs (ORCPT ); Thu, 19 Sep 2019 10:25:48 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=QtZJxbBWOo2zdTo9y6t2qjUifGyNBoCxy5eQB4Jph5JL/vN4VPOyBwWRGlT9Ps5zGewZEWKkaWDstG8g2VPtpbkqxO+4YvX3TwNRBSzRq5wvNaWtKTPcpnY9xbcj/MNMmC8C4hoq3aM9h4PWsDoxteo2cJwv8JfVkHnRtqm5HXl9gVlKQ8a6G5FvuBYS9ntnWBO4M0qYtq6zy38uVpxOb2KudpORAh6PsF6bI5HD5/3U679TAD2wW8Va8PKddaDxvOAfIAC+2lld/Pzd4qW8SG/Xw2w4jZMroUDWtAhoqa+OvGQZDgSYNw1rWYj0uFgaoFkIJMHn0u24wDYG5y8H4Q== 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=vepLWwuZKmOYIAkfD59Q9g/iLxjzCxYdE3KzbH2HEvQ=; b=nE6RQXrpXCboCynp0qnem0TqTfdYeKFWQT1VSoIlu9a7jcrhtTVRTZwSktqgssQVrIP+/FEUbTYGVxNSLidy4yoc7Zv6J40tMCv4QeEHf+Xhx4mZX8VWrxURKRWGfi10WU5EPYMhnjw5CZ1SIHTI3C8+6fUwUxXlNsv/j4c1HexeB5JK34WBoOtdujPJQP8lxAQ2Bv0qC6v2co2A+XsCh65c5NpPsR91hDRAjOa2p9G0AxnI3J/0KH1eWCJRtcyIZysoHx/YQZoiao1Gcm4MB0xkzP0WhqTUJTYXGYWGuHZYu+dQg6bL82F9ismrFmhPAh8eZpUx2i+j4y60Cbb+Lg== 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=vepLWwuZKmOYIAkfD59Q9g/iLxjzCxYdE3KzbH2HEvQ=; b=Z5laYT6pmM5F3Aj8NXknqAcSXTan0fLQ8YB0Fm4+Pugx/TC6eWXVoDiftaly+rms4SSEdxsyfPTk9BtsCze+l429g3iJpNLMnX9UNvaOIRDpQ/m1R+uDsKlOOo40180bb1wGz6WMAYLXDeGSTirwQmhPEiqgq0Wayrj4shuXGOI= Received: from MN2PR11MB4063.namprd11.prod.outlook.com (20.179.149.217) by MN2PR11MB4144.namprd11.prod.outlook.com (20.179.150.210) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2284.20; Thu, 19 Sep 2019 14:25: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 14:25: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 v3 12/20] staging: wfx: add HIF commands helpers Thread-Topic: [PATCH v3 12/20] staging: wfx: add HIF commands helpers Thread-Index: AQHVbvYeO1WTwA1dHUa+Z8LbLgpipw== Date: Thu, 19 Sep 2019 14:25:43 +0000 Message-ID: <20190919142527.31797-13-Jerome.Pouiller@silabs.com> References: <20190919142527.31797-1-Jerome.Pouiller@silabs.com> In-Reply-To: <20190919142527.31797-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: 09eadac1-2bac-42ad-cdde-08d73d0d41c5 x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600167)(711020)(4605104)(1401327)(2017052603328)(7193020);SRVR:MN2PR11MB4144; x-ms-traffictypediagnostic: MN2PR11MB4144: 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)(136003)(396003)(376002)(366004)(39850400004)(346002)(199004)(189003)(86362001)(66066001)(66574012)(2616005)(14454004)(478600001)(54906003)(8676002)(71190400001)(36756003)(71200400001)(7736002)(316002)(305945005)(110136005)(6436002)(256004)(81156014)(14444005)(6486002)(6512007)(186003)(11346002)(91956017)(486006)(446003)(26005)(2501003)(99286004)(66446008)(66946007)(76176011)(476003)(66556008)(64756008)(66476007)(4326008)(102836004)(6506007)(107886003)(5660300002)(30864003)(76116006)(25786009)(81166006)(1076003)(8936002)(2906002)(3846002)(6116002)(579004);DIR:OUT;SFP:1101;SCL:1;SRVR:MN2PR11MB4144;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: oInIF1BDOnmU5r/00DGBXQ2+wgfSiwrbgiVVF1pJ5y2g/sa9Xb1ywQRKb9l1mtDw0A0ljiNeFabEmPKcp+OnI5edP247jAFDOVfgBN6MB7gqGVa+m859M6AxX57GFSr0u5Nz8f+eMkWXi0T0HF8oJCc40FE8YUUiVPeA+EGCkGpUlRw9iGs7VciZvLIn92vgnCmjxkRyJc7U9gYA8Gtvyt9izLbK2zHpZP5S81wQLtMg4A2RksOeGLt7tOmzLSBClgJHZJHyOrlIkiu7vk1pIBFmnGyyjm35Mfz4UypevC+3sc0c7Nf8istDnZ/lciAPB95s3CT2aheHaU0i3LnrJuoLqwH3+NghpbUBg1F8LmcEQ6rD7+LMYO61Mj6e/bvOXSpC3gicU1c1wkeXpCp///NbCa9A2VTE82Rai5myjWg= Content-ID: <64A51D2FAC6E5B49982934C51FEDB6E9@namprd11.prod.outlook.com> MIME-Version: 1.0 X-OriginatorOrg: silabs.com X-MS-Exchange-CrossTenant-Network-Message-Id: 09eadac1-2bac-42ad-cdde-08d73d0d41c5 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Sep 2019 14:25:43.4071 (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: cmxW1P9AB0PMD0ouudbjbx935e7e2Go2IcTtGoMMFJBzinYU6hS4HXpDUHsub6RUDbBGKQrIAyHupaGnBHmycg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR11MB4144 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 +++++++++++++++++++++++ 3 files changed, 689 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