Driver main file to initialize and De-initialize the Kernel module in the kernel. Also maintains some of the core driver structures, functions and common module param registrations. Signed-off-by: Gokul Sivakumar --- drivers/net/wireless/infineon/inffmac/main.c | 821 +++++++++++++++++++ drivers/net/wireless/infineon/inffmac/main.h | 314 +++++++ 2 files changed, 1135 insertions(+) create mode 100644 drivers/net/wireless/infineon/inffmac/main.c create mode 100644 drivers/net/wireless/infineon/inffmac/main.h diff --git a/drivers/net/wireless/infineon/inffmac/main.c b/drivers/net/wireless/infineon/inffmac/main.c new file mode 100644 index 000000000000..9b9d0c4f1409 --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/main.c @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Copyright (c) 2025-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "main.h" +#include "net.h" +#include "cfg80211.h" +#include "utils.h" +#include "chan.h" +#include "debug.h" +#include "bus_proto.h" +#include "sdio.h" +#include "chip.h" +#include "firmware.h" +#include "dev_cmd.h" +#include "dev_evt.h" +#include "feature.h" +#include "interface.h" + +#define INFF_DEFAULT_SCAN_CHANNEL_TIME 40 +#define INFF_DEFAULT_SCAN_UNASSOC_TIME 40 + +/* default boost value for RSSI_DELTA in preferred join selection */ +#define INFF_JOIN_PREF_RSSI_BOOST 8 + +/* Debug level configuration. See debug.h for bits, sysfs modifiable */ +int inff_msg_level; +module_param_named(debug, inff_msg_level, int, 0600); +MODULE_PARM_DESC(debug, "Level of debug output"); + +static char inff_firmware_path[INFF_FW_ALTPATH_LEN]; +module_param_string(alternative_fw_path, inff_firmware_path, + INFF_FW_ALTPATH_LEN, 0400); +MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path"); + +static int inff_fcmode = 2; +module_param_named(fcmode, inff_fcmode, int, 0); +MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); + +static int inff_afq_enable = 1; +module_param_named(afq_enable, inff_afq_enable, int, 0); +MODULE_PARM_DESC(afq_enable, "Enable AFQ txstatus mechanism"); + +static int inff_reboot_callback(struct notifier_block *this, unsigned long code, void *unused); +static struct notifier_block inff_reboot_notifier = { + .notifier_call = inff_reboot_callback, + .priority = 1, +}; + +#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950) + +bool inff_socitype_is_ai(struct inff_bus *bus) +{ + if (bus->chip_pub) + return (bus->chip_pub->socitype == INFF_SOCI_AI); + + return false; +} + +bool inff_socitype_is_cp(struct inff_bus *bus) +{ + if (bus->chip_pub) + return (bus->chip_pub->socitype == INFF_SOCI_CP); + + return false; +} + +char *inff_ifname(struct inff_if *ifp) +{ + if (!ifp) + return ""; + + if (ifp->ndev) + return ifp->ndev->name; + + return ""; +} + +void __inff_err(struct device *dev, const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + if (dev) + dev_err(dev, "%s: %pV", func, &vaf); + else + pr_err("%s: %pV", func, &vaf); + va_end(args); +} + +struct inff_if *inff_get_ifp(struct inff_pub *drvr, int ifidx) +{ + struct inff_if *ifp; + s32 bsscfgidx; + + if (ifidx < 0 || ifidx >= INFF_MAX_IFS) { + iphy_err(drvr, "ifidx %d out of range\n", ifidx); + return NULL; + } + + ifp = NULL; + bsscfgidx = drvr->if2bss[ifidx]; + if (bsscfgidx >= 0) + ifp = drvr->iflist[bsscfgidx]; + + return ifp; +} + +int inff_rx_hdrpull(struct inff_pub *drvr, struct sk_buff *skb, + struct inff_if **ifp) +{ + int ret; + + /* process and remove protocol-specific header */ + ret = inff_proto_hdrpull(drvr, true, skb, ifp); + + if (ret || !(*ifp) || !(*ifp)->ndev) { + if (ret != -ENODATA && *ifp && (*ifp)->ndev) + (*ifp)->ndev->stats.rx_errors++; + inff_pkt_buf_free_skb(skb); + return -ENODATA; + } + + skb->protocol = eth_type_trans(skb, (*ifp)->ndev); + inff_dbg(DATA, "protocol: 0x%04X\n", skb->protocol); + + return 0; +} + +void inff_txfinalize(struct inff_if *ifp, struct sk_buff *txp, bool success) +{ + if (!txp) { + if (!success && ifp && ifp->ndev) + ifp->ndev->stats.tx_errors++; + return; + } + + if (!ifp) { + inff_pkt_buf_free_skb(txp); + return; + } + + if (txp->data) { + struct ethhdr *eh = (struct ethhdr *)(txp->data); + u16 type = ntohs(eh->h_proto); + + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + /* Adding comment to avoid WARNING */ + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); + } + } + + if (!success && ifp->ndev) + ifp->ndev->stats.tx_errors++; + + inff_pkt_buf_free_skb(txp); +} + +void inff_fw_crashed(struct device *dev) +{ + struct inff_bus *bus_if = dev_get_drvdata(dev); + struct inff_pub *drvr = bus_if->drvr; + + iphy_err(drvr, "Firmware has halted or crashed\n"); + + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); +} + +static int inff_get_pend_8021x_cnt(struct inff_if *ifp) +{ + return atomic_read(&ifp->pend_8021x_cnt); +} + +int inff_netdev_wait_pend8021x(struct inff_if *ifp) +{ + struct inff_pub *drvr = ifp->drvr; + int err; + + err = wait_event_timeout(ifp->pend_8021x_wait, + !inff_get_pend_8021x_cnt(ifp), + MAX_WAIT_FOR_8021X_TX); + + if (!err) { + iphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n"); + atomic_set(&ifp->pend_8021x_cnt, 0); + } + + return !err; +} + +static int +inff_psm_watchdog_notify(struct inff_if *ifp, + const struct inff_event_msg *evtmsg, + void *data) +{ + struct inff_pub *drvr = ifp->drvr; + + iphy_err(drvr, "PSM's watchdog has fired bsscfgidx=%d !\n", + ifp->bsscfgidx); + + return 0; +} + +void +inff_set_joinpref_default(struct inff_if *ifp) +{ + struct inff_pub *drvr = ifp->drvr; + struct inff_join_pref_params join_pref_params[2]; + int err; + + /* Setup join_pref to select target by RSSI (boost on 5GHz) */ + join_pref_params[0].type = INFF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = INFF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + + join_pref_params[1].type = INFF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = inff_fwcmd_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + iphy_err(drvr, "Set join_pref error (%d)\n", err); +} + +static int +inff_download_clm_blob(struct inff_if *ifp, u16 flag, + struct inff_dload_data_le *dload_buf, + u32 len) +{ + s32 err; + + flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT); + dload_buf->flag = cpu_to_le16(flag); + dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); + dload_buf->len = cpu_to_le32(len); + dload_buf->crc = cpu_to_le32(0); + + err = inff_fwcmd_iovar_data_set(ifp, "clmload", dload_buf, + struct_size(dload_buf, data, len)); + + return err; +} + +static int +inff_process_clm_blob(struct inff_if *ifp) +{ + struct inff_pub *drvr = ifp->drvr; + struct inff_bus *bus = drvr->bus_if; + struct inff_dload_data_le *chunk_buf; + const struct firmware *clm = NULL; + u32 chunk_len; + u32 datalen; + u32 cumulative_len; + u16 dl_flag = DL_BEGIN; + u32 status = 0; + s32 err; + + err = inff_bus_get_blob(bus, &clm, INFF_BLOB_CLM); + if (err || !clm) { + inff_info("no clm_blob available (err=%d), device may have limited channels available\n", + err); + return 0; + } + + chunk_buf = kzalloc_flex(*chunk_buf, data, MAX_CHUNK_LEN); + if (!chunk_buf) { + err = -ENOMEM; + goto done; + } + + datalen = clm->size; + cumulative_len = 0; + do { + if (datalen > MAX_CHUNK_LEN) { + chunk_len = MAX_CHUNK_LEN; + } else { + chunk_len = datalen; + dl_flag |= DL_END; + } + memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len); + + err = inff_download_clm_blob(ifp, dl_flag, chunk_buf, chunk_len); + + dl_flag &= ~DL_BEGIN; + + cumulative_len += chunk_len; + datalen -= chunk_len; + } while ((datalen > 0) && (err == 0)); + + if (err) { + iphy_err(drvr, "clmload (%zu byte file) failed (%d)\n", + clm->size, err); + /* Retrieve clmload_status and print */ + err = inff_fwcmd_iovar_int_get(ifp, "clmload_status", &status); + if (err) + iphy_err(drvr, "get clmload_status failed (%d)\n", err); + else + inff_dbg(INFO, "clmload_status=%d\n", status); + err = -EIO; + } + + kfree(chunk_buf); +done: + release_firmware(clm); + return err; +} + +int +inff_set_cur_etheraddr(struct inff_if *ifp, const u8 *addr) +{ + s32 err; + + err = inff_fwcmd_iovar_data_set(ifp, "cur_etheraddr", addr, ETH_ALEN); + if (err < 0) + iphy_err(ifp->drvr, "Setting cur_etheraddr failed, %d\n", err); + + return err; +} + +/* On some boards there is no eeprom to hold the nvram, in this case instead + * a board specific nvram is loaded from /lib/firmware. On most boards the + * macaddr setting in the /lib/firmware nvram file is ignored because the + * wifibt chip has a unique MAC programmed into the chip itself. + * But in some cases the actual MAC from the /lib/firmware nvram file gets + * used, leading to MAC conflicts. + * The MAC addresses in the troublesome nvram files seem to all come from + * the same nvram file template, so we only need to check for 1 known + * address to detect this. + */ +static const u8 inff_default_mac_address[ETH_ALEN] = { + 0x00, 0x90, 0x4c, 0xc5, 0x12, 0x38 +}; + +static int +inff_set_preinit_dcmds(struct inff_if *ifp) +{ + struct inff_pub *drvr = ifp->drvr; + s8 eventmask[INFF_EVENTING_MASK_LEN]; + u8 buf[INFF_DCMD_SMLEN]; + struct inff_bus *bus; + struct inff_rev_info_le revinfo; + struct inff_rev_info *ri; + struct inff_wlc_version_le wlc_ver; + char *clmver; + char *ptr; + s32 err; + + if (is_valid_ether_addr(ifp->mac_addr)) { + /* set mac address */ + err = inff_set_cur_etheraddr(ifp, ifp->mac_addr); + if (err < 0) + goto done; + } else { + /* retrieve mac address */ + err = inff_fwcmd_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, + sizeof(ifp->mac_addr)); + if (err < 0) { + iphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err); + goto done; + } + + if (ether_addr_equal_unaligned(ifp->mac_addr, inff_default_mac_address)) { + iphy_err(drvr, "Default MAC is used, replacing with random MAC to avoid conflicts\n"); + eth_random_addr(ifp->mac_addr); + ifp->ndev->addr_assign_type = NET_ADDR_RANDOM; + err = inff_set_cur_etheraddr(ifp, ifp->mac_addr); + if (err < 0) + goto done; + } + } + + memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN); + + bus = ifp->drvr->bus_if; + ri = &ifp->drvr->revinfo; + + err = inff_fwcmd_data_get(ifp, INFF_C_GET_REVINFO, + &revinfo, sizeof(revinfo)); + if (err < 0) { + iphy_err(drvr, "retrieving revision info failed, %d\n", err); + strscpy(ri->chipname, "UNKNOWN", sizeof(ri->chipname)); + } else { + ri->vendorid = le32_to_cpu(revinfo.vendorid); + ri->deviceid = le32_to_cpu(revinfo.deviceid); + ri->radiorev = le32_to_cpu(revinfo.radiorev); + ri->corerev = le32_to_cpu(revinfo.corerev); + ri->boardid = le32_to_cpu(revinfo.boardid); + ri->boardvendor = le32_to_cpu(revinfo.boardvendor); + ri->boardrev = le32_to_cpu(revinfo.boardrev); + ri->driverrev = le32_to_cpu(revinfo.driverrev); + ri->ucoderev = le32_to_cpu(revinfo.ucoderev); + ri->bus = le32_to_cpu(revinfo.bus); + ri->phytype = le32_to_cpu(revinfo.phytype); + ri->phyrev = le32_to_cpu(revinfo.phyrev); + ri->anarev = le32_to_cpu(revinfo.anarev); + ri->chippkg = le32_to_cpu(revinfo.chippkg); + ri->nvramrev = le32_to_cpu(revinfo.nvramrev); + + /* use revinfo if not known yet */ + if (!bus->chip) { + bus->chip = le32_to_cpu(revinfo.chipnum); + bus->chiprev = le32_to_cpu(revinfo.chiprev); + } + } + ri->result = err; + + if (bus->chip) + inff_chip_name(bus->chip, bus->chiprev, + ri->chipname, sizeof(ri->chipname)); + + if (inff_socitype_is_ai(drvr->bus_if)) { + /* Download CLM BLOB */ + err = inff_process_clm_blob(ifp); + if (err < 0) { + iphy_err(drvr, "download CLM blob file failed, %d\n", err); + goto done; + } + } + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + err = inff_fwcmd_iovar_data_get(ifp, "ver", buf, sizeof(buf)); + if (err < 0) { + iphy_err(drvr, "Retrieving version information failed, %d\n", + err); + goto done; + } + buf[sizeof(buf) - 1] = '\0'; + ptr = (char *)buf; + strsep(&ptr, "\n"); + + /* Print fw version info */ + inff_info("Firmware: %s %s\n", ri->chipname, buf); + + /* locate firmware version number for ethtool */ + ptr = strrchr(buf, ' '); + if (!ptr) { + iphy_err(drvr, "Retrieving version number failed"); + goto done; + } + strscpy(ifp->drvr->fwver, ptr + 1, sizeof(ifp->drvr->fwver)); + + /* Get wlc interface version, set to 0 for legacy chip + * that is not supporting wlc_ver iovar + */ + err = inff_fwcmd_iovar_data_get(ifp, "wlc_ver", &wlc_ver, sizeof(wlc_ver)); + if (err < 0) { + ifp->drvr->wlc_ver.wlc_ver_major = 0; + ifp->drvr->wlc_ver.wlc_ver_minor = 0; + } else { + ifp->drvr->wlc_ver.wlc_ver_major = le16_to_cpu(wlc_ver.wlc_ver_major); + ifp->drvr->wlc_ver.wlc_ver_minor = le16_to_cpu(wlc_ver.wlc_ver_minor); + } + inff_dbg(TRACE, "wlc interface version, major=%d, minor=%d\n", + ifp->drvr->wlc_ver.wlc_ver_major, + ifp->drvr->wlc_ver.wlc_ver_minor); + + /* Query for 'clmver' to get CLM version info from firmware */ + if (inff_socitype_is_ai(drvr->bus_if)) { + memset(buf, 0, sizeof(buf)); + err = inff_fwcmd_iovar_data_get(ifp, "clmver", buf, sizeof(buf)); + if (err) { + inff_dbg(TRACE, "retrieving clmver failed, %d\n", err); + } else { + buf[sizeof(buf) - 1] = '\0'; + clmver = (char *)buf; + + /* Replace all newline/linefeed characters with space + * character + */ + strreplace(clmver, '\n', ' '); + + /* store CLM version for adding it to revinfo debugfs file */ + memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver)); + + inff_dbg(INFO, "CLM version = %s\n", clmver); + } + } + + /* set apsta */ + err = inff_fwcmd_iovar_int_set(ifp, "apsta", 1); + if (err) + inff_info("failed setting apsta, %d\n", err); + + /* set mpc */ + err = inff_fwcmd_iovar_int_set(ifp, "mpc", 1); + if (err) { + iphy_err(drvr, "failed setting mpc\n"); + goto done; + } + + inff_set_joinpref_default(ifp); + + /* Setup event_msgs, enable E_IF */ + err = inff_fwcmd_iovar_data_get(ifp, "event_msgs", eventmask, + INFF_EVENTING_MASK_LEN); + if (err) { + iphy_err(drvr, "Get event_msgs error (%d)\n", err); + goto done; + } + setbit(eventmask, INFF_E_IF); + err = inff_fwcmd_iovar_data_set(ifp, "event_msgs", eventmask, + INFF_EVENTING_MASK_LEN); + if (err) { + iphy_err(drvr, "Set event_msgs error (%d)\n", err); + goto done; + } + + if (inff_socitype_is_ai(drvr->bus_if)) { + /* Setup default scan channel time */ + err = inff_fwcmd_int_set(ifp, INFF_C_SET_SCAN_CHANNEL_TIME, + INFF_DEFAULT_SCAN_CHANNEL_TIME); + if (err) { + iphy_err(drvr, "INFF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", + err); + goto done; + } + + /* Setup default scan unassoc time */ + err = inff_fwcmd_int_set(ifp, INFF_C_SET_SCAN_UNASSOC_TIME, + INFF_DEFAULT_SCAN_UNASSOC_TIME); + if (err) { + iphy_err(drvr, "INFF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", + err); + goto done; + } + } + /* Enable tx beamforming, errors can be ignored (not supported) */ + (void)inff_fwcmd_iovar_int_set(ifp, "txbf", 1); + err = inff_fwcmd_iovar_int_set(ifp, "chanspec", 0x1001); + if (err < 0) { + iphy_err(drvr, "Initial Channel failed %d\n", err); + goto done; + } + +done: + return err; +} + +static int +inff_revinfo_read(struct seq_file *s, void *data) +{ + struct inff_bus *bus_if = dev_get_drvdata(s->private); + struct inff_rev_info *ri = &bus_if->drvr->revinfo; + char drev[INFF_DOTREV_LEN]; + char brev[INFF_BOARDREV_LEN]; + + seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid); + seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid); + seq_printf(s, "radiorev: %s\n", inff_dotrev_str(ri->radiorev, drev)); + seq_printf(s, "chip: %s\n", ri->chipname); + seq_printf(s, "chippkg: %u\n", ri->chippkg); + seq_printf(s, "corerev: %u\n", ri->corerev); + seq_printf(s, "boardid: 0x%04x\n", ri->boardid); + seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor); + seq_printf(s, "boardrev: %s\n", inff_boardrev_str(ri->boardrev, brev)); + seq_printf(s, "driverrev: %s\n", inff_dotrev_str(ri->driverrev, drev)); + seq_printf(s, "ucoderev: %u\n", ri->ucoderev); + seq_printf(s, "bus: %u\n", ri->bus); + seq_printf(s, "phytype: %u\n", ri->phytype); + seq_printf(s, "phyrev: %u\n", ri->phyrev); + seq_printf(s, "anarev: %u\n", ri->anarev); + seq_printf(s, "nvramrev: %08x\n", ri->nvramrev); + + seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver); + + return 0; +} + +int +inff_bus_attach(struct device *dev) +{ + struct inff_bus *bus_if = dev_get_drvdata(dev); + struct inff_pub *drvr = bus_if->drvr; + struct inff_if *pri_ifp; + struct inff_if *ifp_ap = NULL; + int ret = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) + drvr->if2bss[i] = INFF_BSSIDX_INVALID; + + mutex_init(&drvr->proto_block); + + /* Link to bus module */ + drvr->hdrlen = 0; + + drvr->req_mpc = 1; + /* Attach and link in the protocol */ + ret = inff_bus_proto_attach(drvr); + if (ret < 0) { + iphy_err(drvr, "inff_prot_attach failed\n"); + goto fail; + } + + if (inff_socitype_is_ai(bus_if)) { + /* Attach to events important for core code */ + inff_fwevt_register(drvr, INFF_E_PSM_WATCHDOG, + inff_psm_watchdog_notify); + /* attach firmware event handler */ + inff_fwevt_attach(drvr); + } else if (inff_socitype_is_cp(bus_if)) { + ret = inff_cpevt_attach(drvr); + if (ret != 0) { + iphy_err(drvr, "Event attach failed.!\n"); + goto fail; + } + } + + if (is_valid_ether_addr(user_mac_addr)) + memcpy(drvr->settings->mac, user_mac_addr, ETH_ALEN); + /* add primary netdev in station mode */ + pri_ifp = inff_net_add_if(drvr, INFF_INTERFACE_PRI_BSSCFGIDX, + INFF_INTERFACE_PRI_IFIDX, "wlan%d", + is_valid_ether_addr(drvr->settings->mac) ? + drvr->settings->mac : NULL, + NL80211_IFTYPE_STATION); + if (IS_ERR(pri_ifp)) { + ret = PTR_ERR(pri_ifp); + goto fail; + } + + if (inff_socitype_is_cp(bus_if)) { + /* add netdev for SoftAP mode */ + ifp_ap = inff_net_add_if(drvr, INFF_INTERFACE_SEC_BSSCFGIDX, + INFF_INTERFACE_SEC_IFIDX, "wlan%d", + NULL, NL80211_IFTYPE_AP); + if (IS_ERR(ifp_ap)) { + ret = PTR_ERR(ifp_ap); + goto fail; + } + } + + if (inff_socitype_is_ai(bus_if)) { + /* signal bus ready */ + inff_bus_change_state(bus_if, INFF_BUS_UP); + + /* do bus specific preinit here */ + ret = inff_bus_preinit(bus_if); + if (ret < 0) + goto fail; + + ret = inff_bus_set_fcmode(bus_if); + /* Set fcmode = 0 for PCIe */ + if (ret < 0) + drvr->settings->fcmode = 0; + + ret = inff_proto_init_done(drvr); + if (ret < 0) + goto fail; + + inff_proto_add_if(drvr, pri_ifp); + } + + inff_feat_attach(drvr); + + /* Bus is ready, do any initialization */ + ret = inff_set_preinit_dcmds(pri_ifp); + if (ret < 0) + goto fail; + + drvr->config = inff_cfg80211_attach(drvr, drvr->ops); + if (!drvr->config) { + ret = -ENOMEM; + goto fail; + } + + ret = inff_net_attach(pri_ifp, false); + if (ret) + goto fail; + + if (inff_socitype_is_cp(bus_if)) { + ret = inff_net_attach(ifp_ap, false); + if (ret) + goto fail; + } + + /* populate debugfs */ + inff_debugfs_add_entry(drvr, "revinfo", inff_revinfo_read); + inff_debugfs_add_entry(drvr, "parameter", inff_debugfs_param_read); + inff_feat_debugfs_create(drvr); + inff_bus_proto_debugfs_create(drvr); + + goto exit; +fail: + iphy_err(drvr, "dongle is not responding: err=%d\n", + ret); + inff_bus_detach(dev); +exit: + return ret; +} + +void +inff_bus_detach(struct device *dev) +{ + s32 i; + struct inff_bus *bus_if = dev_get_drvdata(dev); + struct inff_pub *drvr = bus_if->drvr; + + if (!drvr) + return; + + if (inff_socitype_is_ai(bus_if)) + inff_fwevt_detach(drvr); + else if (inff_socitype_is_cp(bus_if)) + inff_cpevt_detach(drvr); + + inff_bus_stop(bus_if); + + inff_bus_change_state(bus_if, INFF_BUS_DOWN); + + /* make sure primary interface removed last */ + for (i = INFF_MAX_IFS - 1; i > -1; i--) { + if (drvr->iflist[i]) + inff_net_del_if(drvr, drvr->iflist[i], false); + } + + inff_bus_proto_detach(drvr); + + if (drvr->config) { + inff_cfg80211_detach(drvr->config); + drvr->config = NULL; + } +} + +int inff_debugfs_param_read(struct seq_file *s, void *data) +{ + struct inff_bus *bus_if = dev_get_drvdata(s->private); + + seq_printf(s, "%-20s: %s\n", "Name", "Value"); + seq_printf(s, "%-20s: 0x%x\n", "debug", inff_msg_level); + seq_printf(s, "%-20s: %s\n", "alternative_fw_path", inff_firmware_path); + seq_printf(s, "%-20s: %d\n", "fcmode", bus_if->drvr->settings->fcmode); + seq_printf(s, "%-20s: %d\n", "afq_enable", !!inff_afq_enable); + + return 0; +} + +struct inff_mp_device *inff_get_module_param(struct device *dev) +{ + struct inff_mp_device *settings; + + settings = kzalloc_obj(*settings, GFP_ATOMIC); + if (!settings) + return NULL; + + /* start by using the module parameters */ + inff_dbg(INFO, "debug: 0x%x\n", inff_msg_level); + strscpy(settings->firmware_path, inff_firmware_path, + INFF_FW_ALTPATH_LEN); + inff_dbg(INFO, "alternative_fw_path: %s\n", settings->firmware_path); + + settings->fcmode = inff_fcmode; + inff_dbg(INFO, "fcmode: %d\n", settings->fcmode); + settings->afq_enable = !!inff_afq_enable; + inff_dbg(INFO, "afq_enable: %d\n", settings->afq_enable); + + return settings; +} + +void inff_release_module_param(struct inff_mp_device *module_param) +{ + kfree(module_param); +} + +static int +inff_reboot_callback(struct notifier_block *this, unsigned long code, void *unused) +{ + inff_dbg(INFO, "code = %ld\n", code); + if (code == SYS_RESTART) + inff_sdio_exit(); + + return NOTIFY_DONE; +} + +static int __init inff_module_init(void) +{ + int err; + + err = inff_sdio_register(); + if (err) + return err; + + register_reboot_notifier(&inff_reboot_notifier); + + return 0; +} + +static void __exit inff_module_exit(void) +{ + inff_sdio_exit(); + + unregister_reboot_notifier(&inff_reboot_notifier); +} + +module_init(inff_module_init); +module_exit(inff_module_exit); + +MODULE_AUTHOR("Infineon Technologies AG"); +MODULE_DESCRIPTION("Infineon 802.11 wireless LAN fullmac driver."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/infineon/inffmac/main.h b/drivers/net/wireless/infineon/inffmac/main.h new file mode 100644 index 000000000000..6f68838612ba --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/main.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Copyright (c) 2025-2026, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#ifndef INFF_MAIN_H +#define INFF_MAIN_H + +#include +#include +#include + +#include "dev_evt.h" +#include "dev_cmd.h" +#include "feature.h" + +/* The interval to poll console */ +#define INFF_CONSOLE_INTERVAL 500 +#define INFF_CPUSS_CONSOLE_INTERVAL 100 + +/* The maximum console interval value (5 mins) */ +#define MAX_CONSOLE_INTERVAL (5 * 60 * 1000) + +/* + * Priority definitions according 802.1D + */ +#define PRIO_8021D_NONE 2 +#define PRIO_8021D_BK 1 +#define PRIO_8021D_BE 0 +#define PRIO_8021D_EE 3 +#define PRIO_8021D_CL 4 +#define PRIO_8021D_VI 5 +#define PRIO_8021D_VO 6 +#define PRIO_8021D_NC 7 + +#define MAXPRIO 7 +#define NUMPRIO (MAXPRIO + 1) + +/* Bit masks for radio disabled status - returned by WL_GET_RADIO */ + +#define WL_RADIO_SW_DISABLE BIT(0) +#define WL_RADIO_HW_DISABLE BIT(1) +/* some countries don't support any channel */ +#define WL_RADIO_COUNTRY_DISABLE BIT(3) + +/* Override bit for SET_TXPWR. if set, ignore other level limits */ +#define WL_TXPWR_OVERRIDE BIT(31) + +/* + * Sonics Configuration Space Registers. + */ + +/* core sbconfig regs are top 256bytes of regs */ +#define SBCONFIGOFF 0xf00 + +#define TOE_TX_CSUM_OL 0x00000001 +#define TOE_RX_CSUM_OL 0x00000002 + +/* For supporting multiple interfaces */ +#define INFF_MAX_IFS 16 + +/* Small, medium and maximum buffer size for dcmd + */ +#define INFF_DCMD_SMLEN 256 +#define INFF_DCMD_MEDLEN 1536 +#define INFF_DCMD_MAXLEN 8192 + +/* IOCTL from host to device are limited in length. A device can only handle + * ethernet frame size. This limitation is to be applied by protocol layer. + */ +#define INFF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN) + +#define INFF_AMPDU_RX_REORDER_MAXFLOWS 256 + +/* Length of firmware version string stored for + * ethtool driver info which uses 32 bytes as well. + */ +#define INFF_DRIVER_FIRMWARE_VERSION_LEN 32 + +#define NDOL_MAX_ENTRIES 8 + +#define INFF_FW_ALTPATH_LEN 256 + +#define INFFMAC_DISABLE 0 +#define INFFMAC_ENABLE 1 +#define INFFMAC_AUTO 2 + +#define INFF_DEFAULT_SDIO_IDLE_CONFIG 2 +/* Keeping these macro definition here because these are defined in mmc drivers. + * So for 3rd party mmc, fmac build should not fail due to build error. + */ + +/* SDIO IDLECLOCK Support - reusing pm_caps */ +#ifndef SDIO_IDLECLOCK_DIS +#define SDIO_IDLECLOCK_DIS BIT(2) /* Start SDClock */ +#define SDIO_IDLECLOCK_EN BIT(3) /* Stop SDClock */ +#define SDIO_SDMODE_1BIT BIT(4) /* Set 1-bit Bus mode */ +#define SDIO_SDMODE_4BIT BIT(5) /* Set 4-bit Bus mode */ +#endif /* !SDIO_IDLECLOCK_DIS */ + +extern u8 user_mac_addr[ETH_ALEN]; + +struct inff_sdio_platform_data { + bool oob_irq_supported; + unsigned int oob_irq_nr; + unsigned long oob_irq_flags; + bool broken_sg_support; + unsigned short sd_head_align; + unsigned short sd_sgentry_align; +}; + +/** + * struct inff_mp_device - Device module parameters. + * + * @fcmode: FWS flow control. + * @afq_enable: Enable AFQ txstatus mechanism. + * @country_codes: If available, pointer to struct for translating country codes + * @bus: Bus specific platform data. Only SDIO at the mmoment. + */ +#define INFF_MAX_FEATURE_BYTES DIV_ROUND_UP(INFF_FEAT_LAST, 8) +struct inff_mp_device { + char firmware_path[INFF_FW_ALTPATH_LEN]; + int fcmode; + bool afq_enable; + const char *board_type; + unsigned char mac[ETH_ALEN]; + union { + struct inff_sdio_platform_data sdio; + } bus; +}; + +/** + * struct inff_ampdu_rx_reorder - AMPDU receive reorder info + * + * @flow_id: AMPDU flow identifier. + * @cur_idx: last AMPDU index from firmware. + * @exp_idx: expected next AMPDU index. + * @max_idx: maximum amount of packets per AMPDU. + * @pend_pkts: number of packets currently in @pktslots. + * @pktslots: array for ordering AMPDU packets. + */ +struct inff_ampdu_rx_reorder { + u8 flow_id; + u8 cur_idx; + u8 exp_idx; + u8 max_idx; + u8 pend_pkts; + struct sk_buff *pktslots[]; +}; + +/* Forward decls for struct inff_pub (see below) */ +struct inff_proto; /* device communication protocol info */ +struct inff_fws_info; /* firmware signalling info */ +struct inff_mp_device; /* module parameters, device specific */ + +/* + * struct inff_rev_info + * + * The result field stores the error code of the + * revision info request from firmware. For the + * other fields see struct inff_rev_info_le in + * fwil_types.h + */ +struct inff_rev_info { + int result; + u32 vendorid; + u32 deviceid; + u32 radiorev; + u32 corerev; + u32 boardid; + u32 boardvendor; + u32 boardrev; + u32 driverrev; + u32 ucoderev; + u32 bus; + char chipname[12]; + u32 phytype; + u32 phyrev; + u32 anarev; + u32 chippkg; + u32 nvramrev; +}; + +/** wlc interface version */ +struct inff_wlc_version { + /* wlc interface version numbers */ + u16 wlc_ver_major; /**< wlc interface major version number */ + u16 wlc_ver_minor; /**< wlc interface minor version number */ +}; + +/* Common structure for module and instance linkage */ +struct inff_pub { + /* Linkage ponters */ + struct inff_bus *bus_if; + struct inff_proto *proto; + struct wiphy *wiphy; + struct cfg80211_ops *ops; + struct inff_cfg80211_info *config; + + /* Internal inff items */ + uint hdrlen; /* Total INFF header length (proto + bus) */ + + /* Dongle media info */ + char fwver[INFF_DRIVER_FIRMWARE_VERSION_LEN]; + u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ + + struct mac_address addresses[INFF_MAX_IFS]; + + struct inff_if *iflist[INFF_MAX_IFS]; + s32 if2bss[INFF_MAX_IFS]; + + struct mutex proto_block; /* used to protect proto resource */ + unsigned char proto_buf[INFF_DCMD_MAXLEN]; + + struct inff_fwevt_info fwevt; + + struct inff_ampdu_rx_reorder + *reorder_flows[INFF_AMPDU_RX_REORDER_MAXFLOWS]; + + u8 feat_flags[DIV_ROUND_UP(INFF_FEAT_LAST, 8)]; + u32 chip_quirks; + int req_mpc; + + struct inff_rev_info revinfo; + struct inff_wlc_version wlc_ver; +#ifdef DEBUG + struct dentry *dbgfs_dir; +#endif + + struct inff_mp_device *settings; + + struct work_struct bus_reset; + + u8 clmver[INFF_DCMD_SMLEN]; + u8 sta_mac_idx; + + struct inff_dfu *dfu; + struct inff_cpevt_info evi; +}; + +/* forward declarations */ +struct inff_cfg80211_vif; +struct inff_fws_mac_descriptor; + +#define INFF_BSSIDX_INVALID -1 + +/** + * struct inff_if - interface control information. + * + * @drvr: points to device related information. + * @vif: points to cfg80211 specific interface information. + * @ndev: associated network device. + * @multicast_work: wiphy work object for multicast provisioning. + * @fws_desc: interface specific firmware-signalling descriptor. + * @ifidx: interface index in device firmware. + * @bsscfgidx: index of bss associated with this interface. + * @mac_addr: assigned mac address. + * @netif_stop: bitmap indicates reason why netif queues are stopped. + * @netif_stop_lock: spinlock for update netif_stop from multiple sources. + * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. + * @pend_8021x_wait: used for signalling change in count. + * @fwil_fwerr: flag indicating fwil layer should return firmware error codes. + * @fmac_pkt_fwd_en: Driver packet forward enabled for AP Mode. + * @napi: NAPI structure for handling the RX packets received on this interface. + * @napi_gro: NAPI GRO Receive enabled flag. + */ +struct inff_if { + struct inff_pub *drvr; + struct inff_cfg80211_vif *vif; + struct net_device *ndev; + struct wiphy_work multicast_work; + struct inff_fws_mac_descriptor *fws_desc; + int ifidx; + s32 bsscfgidx; + bool isap; + u8 mac_addr[ETH_ALEN]; + u8 netif_stop; + spinlock_t netif_stop_lock; /* used to protect netif_stop resource */ + atomic_t pend_8021x_cnt; + wait_queue_head_t pend_8021x_wait; + bool fwil_fwerr; + bool fmac_pkt_fwd_en; + struct napi_struct napi; + bool napi_gro; + bool connected; +}; + +bool inff_socitype_is_ai(struct inff_bus *bus); +bool inff_socitype_is_cp(struct inff_bus *bus); +/* Return pointer to interface name */ +char *inff_ifname(struct inff_if *ifp); +struct inff_if *inff_get_ifp(struct inff_pub *drvr, int ifidx); +int inff_rx_hdrpull(struct inff_pub *drvr, struct sk_buff *skb, + struct inff_if **ifp); +void inff_txfinalize(struct inff_if *ifp, struct sk_buff *txp, bool success); +/* Indication that firmware has halted or crashed */ +void inff_fw_crashed(struct device *dev); +int inff_netdev_wait_pend8021x(struct inff_if *ifp); + +void inff_set_joinpref_default(struct inff_if *ifp); +int inff_set_cur_etheraddr(struct inff_if *ifp, const u8 *addr); + +/* Indication from bus module regarding presence/insertion of dongle. */ +int inff_bus_attach(struct device *dev); +/* Indication from bus module regarding removal/absence of dongle */ +void inff_bus_detach(struct device *dev); + +int inff_debugfs_param_read(struct seq_file *s, void *data); +struct inff_mp_device *inff_get_module_param(struct device *dev); +void inff_release_module_param(struct inff_mp_device *module_param); +#endif /* INFF_MAIN_H */ -- 2.43.0