From: Jérôme Pouiller <jerome.pouiller@xxxxxxxxxx> 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 <jerome.pouiller@xxxxxxxxxx> --- 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 <linux/skbuff.h> +#include <linux/etherdevice.h> + +#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 <linux/module.h> #include <linux/of.h> +#include <linux/of_net.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/mmc/sdio_func.h> @@ -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 <linux/completion.h> +#include <net/mac80211.h> + #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; }; -- 2.20.1