new file mode 100644
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file contains the main events processing thread.
+ *
+ * Copyright (C) [2022-2025] Renesas Electronics Corporation and/or its affiliates.
+ */
+
+#include <linux/firmware.h>
+
+#include "core.h"
+#include "if.h"
+#include "params.h"
+#include "dbg.h"
+
+#define RA6W_CORE_THREAD_NAME "ra6w_core_thread"
+
+static inline u8 ra6w_core_cmd_to_hndl_type(u8 cmd)
+{
+ if (cmd == RA6W_CMD_DATA_RX)
+ return RA6W_CORE_HNDL_RX;
+
+ if (cmd == RA6W_CMD_DATA_STATUS_RX)
+ return RA6W_CORE_HNDL_STATUS;
+
+ if (cmd >= RA6W_CMD_COMMON_START && cmd < RA6W_CMD_COMMON_MAX)
+ return RA6W_CORE_HNDL_CTRL;
+
+ if (cmd >= RA6W_CMD_FULLMAC_START && cmd < RA6W_CMD_FULLMAC_MAX)
+ return RA6W_CORE_HNDL_CTRL;
+
+ if (cmd >= RA6W_CMD_DBG_START && cmd < RA6W_CMD_DBG_MAX)
+ return RA6W_CORE_HNDL_CTRL;
+
+ if (cmd >= RA6W_CMD_IND_FULLMAC_START && cmd < RA6W_CMD_IND_FULLMAC_MAX)
+ return RA6W_CORE_HNDL_INDI;
+
+ return RA6W_CORE_HNDL_MAX;
+}
+
+static void ra6w_core_event_handler(struct ra6w_core *core)
+{
+ struct ra6w_if *ifp = container_of(core, struct ra6w_if, core);
+ struct ra6w_status *status = &core->status;
+ struct ra6w_indi *indi = &core->indi;
+ struct ra6w_rx *rx = &core->rx;
+ int ret;
+ union ra6w_core_data *data = NULL;
+ struct sk_buff *skb = NULL;
+ u8 cmd;
+
+ do {
+ if (!skb) {
+ skb = dev_alloc_skb(sizeof(*data));
+ if (!skb)
+ return;
+
+ skb_put(skb, sizeof(*data));
+ }
+
+ ret = ra6w_if_read(ifp, skb->data, sizeof(*data));
+ if (ret)
+ break;
+
+ data = (union ra6w_core_data *)skb->data;
+ cmd = ra6w_core_cmd_to_hndl_type(data->rx.cmd);
+ switch (cmd) {
+ case RA6W_CORE_HNDL_RX:
+ ret = ra6w_rx_event_post(rx, skb);
+ if (ret == 0)
+ skb = NULL;
+ break;
+ case RA6W_CORE_HNDL_CTRL:
+ ra6w_ctrl_event_post(&core->ctrl, skb);
+ break;
+ case RA6W_CORE_HNDL_INDI:
+ ret = ra6w_indi_event_post(indi, skb);
+ if (ret == 0)
+ skb = NULL;
+ break;
+ case RA6W_CORE_HNDL_STATUS:
+ ra6w_status_event_post(&core->status, skb);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ } while (!kthread_should_stop() && !ret && !ra6w_status_rx_get(status));
+
+ dev_kfree_skb(skb);
+}
+
+static int core_thread_handler(void *arg)
+{
+ struct ra6w_core *core = arg;
+ int event = 0;
+
+ while (!kthread_should_stop()) {
+ event = ra6w_q_wait(&core->event, RA6W_CORE_EVENT_MASK);
+ if (event & BIT(RA6W_CORE_EVENT_DATA))
+ ra6w_core_event_handler(core);
+
+ if (event & BIT(RA6W_CORE_EVENT_RESET))
+ break;
+
+ atomic_set(&core->event.condition, 0);
+ }
+
+ return 0;
+}
+
+static int ra6w_core_event_init(struct ra6w_core *core)
+{
+ atomic_set(&core->event.condition, 0);
+ init_waitqueue_head(&core->event.wait_queue);
+ core->task = kthread_run(core_thread_handler, core, RA6W_CORE_THREAD_NAME);
+ if (!core->task) {
+ ra6w_err("[%s] kthread_run %s failed\n", __func__, RA6W_CORE_THREAD_NAME);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static void ra6w_core_event_deinit(struct ra6w_core *core)
+{
+ if (!core->task)
+ return;
+
+ atomic_set(&core->event.condition, BIT(RA6W_CORE_EVENT_RESET));
+ kthread_stop(core->task);
+ core->task = NULL;
+}
+
+int ra6w_core_init(struct ra6w_core *core)
+{
+ int ret;
+
+ ret = ra6w_tx_init(&core->tx);
+ if (ret)
+ return ret;
+
+ ra6w_ctrl_init(&core->ctrl);
+
+ ret = ra6w_indi_init(&core->indi);
+ if (ret)
+ goto ctrl_deinit;
+
+ ret = ra6w_rx_init(&core->rx);
+ if (ret)
+ goto indi_deinit;
+
+ ra6w_status_init(&core->status);
+
+ ret = ra6w_core_event_init(core);
+ if (ret)
+ goto rx_deinit;
+
+ return 0;
+
+rx_deinit:
+ ra6w_rx_deinit(&core->rx);
+
+indi_deinit:
+ ra6w_indi_deinit(&core->indi);
+
+ctrl_deinit:
+ ra6w_tx_deinit(&core->tx);
+
+ return ret;
+}
+
+static u8 RENESAS_OUI[] = { 0xd4, 0x3d, 0x39 };
+
+static void ra6w_core_rand_mac_addr_gen(u8 *mac)
+{
+ memcpy(mac, RENESAS_OUI, 3);
+ get_random_bytes(&mac[3], 3);
+}
+
+static const u8 *ra6w_core_find_tag(const u8 *file_data, const u8 *tag_name, u32 tag_name_len)
+{
+ const char *tag_data = NULL;
+
+ if (!tag_name || tag_name_len == 0)
+ return NULL;
+
+ tag_data = strnstr(file_data, tag_name, tag_name_len);
+ if (!tag_data)
+ return NULL;
+
+ return tag_data + tag_name_len;
+}
+
+#define RA6W_CORE_DEFAULT_MAC_NAME "default_mac.ini"
+#define RA6W_CORE_MAC_TAG "MAC_ADDR="
+#define RA6W_CORE_MAC_TAG_LEN strlen(RA6W_CORE_MAC_TAG)
+
+static int ra6w_core_fw_mac_addr_get(struct ra6w_core *core, u8 *mac)
+{
+ struct ra6w_if *ifp = container_of(core, struct ra6w_if, core);
+ const struct firmware *fw = NULL;
+ const u8 *fw_mac = NULL;
+ char path[100] = { 0 };
+ int ret;
+ int n;
+
+ snprintf(path, sizeof(path), "%s/%s", KBUILD_MODNAME, RA6W_CORE_DEFAULT_MAC_NAME);
+
+ ret = request_firmware(&fw, path, &ifp->dev.func->dev);
+ if (ret) {
+ ra6w_warn("request_firmware %s failed: %d\n", path, ret);
+ return -ENOENT;
+ }
+
+ if (RA6W_CORE_MAC_TAG_LEN + RA6W_MAC_ADDR_STR_LEN > fw->size) {
+ ret = -ENOENT;
+ goto free_fw;
+ }
+
+ fw_mac = ra6w_core_find_tag(fw->data, RA6W_CORE_MAC_TAG, RA6W_CORE_MAC_TAG_LEN);
+ if (!fw_mac) {
+ ra6w_warn("Tag %s not found in %s\n", RA6W_CORE_MAC_TAG, path);
+ ret = -ENOENT;
+ goto free_fw;
+ }
+
+ n = sscanf(fw_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5);
+ if (n != ETH_ALEN || !is_valid_ether_addr(mac))
+ ret = -ENOENT;
+
+free_fw:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int ra6w_core_mac_addr_init(struct ra6w_core *core)
+{
+ u8 mac[ETH_ALEN] = { 0 };
+ int ret;
+
+ ret = ra6w_ctrl_otp_mac_addr_get(&core->ctrl, mac);
+ if (ret == 0) {
+ ra6w_info("OTP MAC Address : %pM\n", mac);
+ goto set;
+ }
+
+ ret = ra6w_core_fw_mac_addr_get(core, mac);
+ if (ret == 0) {
+ ra6w_info("FW MAC Address %pM\n", mac);
+ goto set;
+ }
+
+ ra6w_core_rand_mac_addr_gen(mac);
+ ra6w_info("Random MAC Address %pM\n", mac);
+
+set:
+ ether_addr_copy(core->sinfo.default_mac, mac);
+
+ return 0;
+}
+
+int ra6w_core_post_init(struct ra6w_core *core)
+{
+ int ret;
+ struct ra6w_ctrl *ctrl = &core->ctrl;
+
+ ret = ra6w_ctrl_dev_reset(ctrl);
+ if (ret)
+ return ret;
+
+ ret = ra6w_ctrl_update_fw_ver(ctrl);
+ if (ret)
+ return ret;
+
+ return ra6w_core_mac_addr_init(core);
+}
+
+void ra6w_core_deinit(struct ra6w_core *core)
+{
+ ra6w_core_event_deinit(core);
+ ra6w_rx_deinit(&core->rx);
+ ra6w_indi_deinit(&core->indi);
+ ra6w_tx_deinit(&core->tx);
+}