diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2025-08-03 23:00:30 +0200 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2025-08-03 23:00:30 +0200 |
commit | 36ff275483f2fe64a8eafd4c98e1639922e99fe2 (patch) | |
tree | 08ea691381332efaee58268b668b25e4e71ccd42 /target/linux/patches/6.15.6/orinoco.patch | |
parent | 628f2a5655806512a129221a7c8ccc30de5fc647 (diff) |
Diffstat (limited to 'target/linux/patches/6.15.6/orinoco.patch')
-rw-r--r-- | target/linux/patches/6.15.6/orinoco.patch | 12933 |
1 files changed, 12933 insertions, 0 deletions
diff --git a/target/linux/patches/6.15.6/orinoco.patch b/target/linux/patches/6.15.6/orinoco.patch new file mode 100644 index 000000000..6401013fd --- /dev/null +++ b/target/linux/patches/6.15.6/orinoco.patch @@ -0,0 +1,12933 @@ +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/Kconfig linux-6.15.6/drivers/net/wireless/intersil/Kconfig +--- linux-6.15.6.orig/drivers/net/wireless/intersil/Kconfig 2025-07-10 16:08:55.000000000 +0200 ++++ linux-6.15.6/drivers/net/wireless/intersil/Kconfig 2025-08-03 00:20:27.566744589 +0200 +@@ -12,6 +12,7 @@ + + if WLAN_VENDOR_INTERSIL + ++source "drivers/net/wireless/intersil/orinoco/Kconfig" + source "drivers/net/wireless/intersil/p54/Kconfig" + + endif # WLAN_VENDOR_INTERSIL +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/Makefile linux-6.15.6/drivers/net/wireless/intersil/Makefile +--- linux-6.15.6.orig/drivers/net/wireless/intersil/Makefile 2025-07-10 16:08:55.000000000 +0200 ++++ linux-6.15.6/drivers/net/wireless/intersil/Makefile 2025-08-03 00:20:54.161967083 +0200 +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0-only ++obj-$(CONFIG_HERMES) += orinoco/ + obj-$(CONFIG_P54_COMMON) += p54/ +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/airport.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/airport.c +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/airport.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/airport.c 2025-08-03 15:01:59.313131673 +0200 +@@ -0,0 +1,268 @@ ++/* airport.c ++ * ++ * A driver for "Hermes" chipset based Apple Airport wireless ++ * card. ++ * ++ * Copyright notice & release notes in file main.c ++ * ++ * Note specific to airport stub: ++ * ++ * 0.05 : first version of the new split driver ++ * 0.06 : fix possible hang on powerup, add sleep support ++ */ ++ ++#define DRIVER_NAME "airport" ++#define PFX DRIVER_NAME ": " ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/mod_devicetable.h> ++#include <asm/pmac_feature.h> ++ ++#include "orinoco.h" ++ ++#define AIRPORT_IO_LEN (0x1000) /* one page */ ++ ++struct airport { ++ struct macio_dev *mdev; ++ void __iomem *vaddr; ++ unsigned int irq; ++ int irq_requested; ++ int ndev_registered; ++}; ++ ++static int ++airport_suspend(struct macio_dev *mdev, pm_message_t state) ++{ ++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); ++ struct net_device *dev = priv->ndev; ++ struct airport *card = priv->card; ++ unsigned long flags; ++ int err; ++ ++ printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); ++ ++ err = orinoco_lock(priv, &flags); ++ if (err) { ++ printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", ++ dev->name); ++ return 0; ++ } ++ ++ orinoco_down(priv); ++ orinoco_unlock(priv, &flags); ++ ++ disable_irq(card->irq); ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(mdev), 0, 0); ++ ++ return 0; ++} ++ ++static int ++airport_resume(struct macio_dev *mdev) ++{ ++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); ++ struct net_device *dev = priv->ndev; ++ struct airport *card = priv->card; ++ unsigned long flags; ++ int err; ++ ++ printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); ++ ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(mdev), 0, 1); ++ msleep(200); ++ ++ enable_irq(card->irq); ++ ++ priv->hw.ops->lock_irqsave(&priv->lock, &flags); ++ err = orinoco_up(priv); ++ priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); ++ ++ return err; ++} ++ ++static int ++airport_detach(struct macio_dev *mdev) ++{ ++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); ++ struct airport *card = priv->card; ++ ++ if (card->ndev_registered) ++ orinoco_if_del(priv); ++ card->ndev_registered = 0; ++ ++ if (card->irq_requested) ++ free_irq(card->irq, priv); ++ card->irq_requested = 0; ++ ++ if (card->vaddr) ++ iounmap(card->vaddr); ++ card->vaddr = NULL; ++ ++ macio_release_resource(mdev, 0); ++ ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(mdev), 0, 0); ++ ssleep(1); ++ ++ macio_set_drvdata(mdev, NULL); ++ free_orinocodev(priv); ++ ++ return 0; ++} ++ ++static int airport_hard_reset(struct orinoco_private *priv) ++{ ++ /* It would be nice to power cycle the Airport for a real hard ++ * reset, but for some reason although it appears to ++ * re-initialize properly, it falls in a screaming heap ++ * shortly afterwards. */ ++#if 0 ++ struct airport *card = priv->card; ++ ++ /* Vitally important. If we don't do this it seems we get an ++ * interrupt somewhere during the power cycle, since ++ * hw_unavailable is already set it doesn't get ACKed, we get ++ * into an interrupt loop and the PMU decides to turn us ++ * off. */ ++ disable_irq(card->irq); ++ ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(card->mdev), 0, 0); ++ ssleep(1); ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(card->mdev), 0, 1); ++ ssleep(1); ++ ++ enable_irq(card->irq); ++ ssleep(1); ++#endif ++ ++ return 0; ++} ++ ++static int ++airport_attach(struct macio_dev *mdev, const struct of_device_id *match) ++{ ++ struct orinoco_private *priv; ++ struct airport *card; ++ unsigned long phys_addr; ++ struct hermes *hw; ++ ++ if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { ++ printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n"); ++ return -ENODEV; ++ } ++ ++ /* Allocate space for private device-specific data */ ++ priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, ++ airport_hard_reset, NULL); ++ if (!priv) { ++ printk(KERN_ERR PFX "Cannot allocate network device\n"); ++ return -ENODEV; ++ } ++ card = priv->card; ++ ++ hw = &priv->hw; ++ card->mdev = mdev; ++ ++ if (macio_request_resource(mdev, 0, DRIVER_NAME)) { ++ printk(KERN_ERR PFX "can't request IO resource !\n"); ++ free_orinocodev(priv); ++ return -EBUSY; ++ } ++ ++ macio_set_drvdata(mdev, priv); ++ ++ /* Setup interrupts & base address */ ++ card->irq = macio_irq(mdev, 0); ++ phys_addr = macio_resource_start(mdev, 0); /* Physical address */ ++ printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); ++ card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); ++ if (!card->vaddr) { ++ printk(KERN_ERR PFX "ioremap() failed\n"); ++ goto failed; ++ } ++ ++ hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING); ++ ++ /* Power up card */ ++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, ++ macio_get_of_node(mdev), 0, 1); ++ ssleep(1); ++ ++ /* Reset it before we get the interrupt */ ++ hw->ops->init(hw); ++ ++ if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { ++ printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); ++ goto failed; ++ } ++ card->irq_requested = 1; ++ ++ /* Initialise the main driver */ ++ if (orinoco_init(priv) != 0) { ++ printk(KERN_ERR PFX "orinoco_init() failed\n"); ++ goto failed; ++ } ++ ++ /* Register an interface with the stack */ ++ if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) { ++ printk(KERN_ERR PFX "orinoco_if_add() failed\n"); ++ goto failed; ++ } ++ card->ndev_registered = 1; ++ return 0; ++ failed: ++ airport_detach(mdev); ++ return -ENODEV; ++} /* airport_attach */ ++ ++ ++static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION ++ " (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; ++MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); ++MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); ++MODULE_LICENSE("Dual MPL/GPL"); ++ ++static const struct of_device_id airport_match[] = { ++ { ++ .name = "radio", ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, airport_match); ++ ++static struct macio_driver airport_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = airport_match, ++ }, ++ .probe = airport_attach, ++ .remove = airport_detach, ++ .suspend = airport_suspend, ++ .resume = airport_resume, ++}; ++ ++static int __init ++init_airport(void) ++{ ++ printk(KERN_DEBUG "%s\n", version); ++ ++ return macio_register_driver(&airport_driver); ++} ++ ++static void __exit ++exit_airport(void) ++{ ++ macio_unregister_driver(&airport_driver); ++} ++ ++module_init(init_airport); ++module_exit(exit_airport); +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.c +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.c 2025-08-03 16:35:08.118093622 +0200 +@@ -0,0 +1,292 @@ ++/* cfg80211 support ++ * ++ * See copyright notice in main.c ++ */ ++#include <linux/ieee80211.h> ++#include <net/cfg80211.h> ++#include "hw.h" ++#include "main.h" ++#include "orinoco.h" ++ ++#include "cfg.h" ++ ++/* Supported bitrates. Must agree with hw.c */ ++static struct ieee80211_rate orinoco_rates[] = { ++ { .bitrate = 10 }, ++ { .bitrate = 20 }, ++ { .bitrate = 55 }, ++ { .bitrate = 110 }, ++}; ++ ++static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid; ++ ++/* Called after orinoco_private is allocated. */ ++void orinoco_wiphy_init(struct wiphy *wiphy) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ ++ wiphy->privid = orinoco_wiphy_privid; ++ ++ set_wiphy_dev(wiphy, priv->dev); ++} ++ ++/* Called after firmware is initialised */ ++int orinoco_wiphy_register(struct wiphy *wiphy) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ int i, channels = 0; ++ ++ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) ++ wiphy->max_scan_ssids = 1; ++ else ++ wiphy->max_scan_ssids = 0; ++ ++ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); ++ ++ /* TODO: should we set if we only have demo ad-hoc? ++ * (priv->has_port3) ++ */ ++ if (priv->has_ibss) ++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); ++ ++ if (!priv->broken_monitor || force_monitor) ++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); ++ ++ priv->band.bitrates = orinoco_rates; ++ priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates); ++ ++ /* Only support channels allowed by the card EEPROM */ ++ for (i = 0; i < NUM_CHANNELS; i++) { ++ if (priv->channel_mask & (1 << i)) { ++ priv->channels[i].center_freq = ++ ieee80211_channel_to_frequency(i + 1, ++ NL80211_BAND_2GHZ); ++ channels++; ++ } ++ } ++ priv->band.channels = priv->channels; ++ priv->band.n_channels = channels; ++ ++ wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; ++ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; ++ ++ i = 0; ++ if (priv->has_wep) { ++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40; ++ i++; ++ ++ if (priv->has_big_wep) { ++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104; ++ i++; ++ } ++ } ++ if (priv->has_wpa) { ++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP; ++ i++; ++ } ++ wiphy->cipher_suites = priv->cipher_suites; ++ wiphy->n_cipher_suites = i; ++ ++ wiphy->rts_threshold = priv->rts_thresh; ++ if (!priv->has_mwo) ++ wiphy->frag_threshold = priv->frag_thresh + 1; ++ wiphy->retry_short = priv->short_retry_limit; ++ wiphy->retry_long = priv->long_retry_limit; ++ ++ return wiphy_register(wiphy); ++} ++ ++static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, ++ enum nl80211_iftype type, ++ struct vif_params *params) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ int err = 0; ++ unsigned long lock; ++ ++ if (orinoco_lock(priv, &lock) != 0) ++ return -EBUSY; ++ ++ switch (type) { ++ case NL80211_IFTYPE_ADHOC: ++ if (!priv->has_ibss && !priv->has_port3) ++ err = -EINVAL; ++ break; ++ ++ case NL80211_IFTYPE_STATION: ++ break; ++ ++ case NL80211_IFTYPE_MONITOR: ++ if (priv->broken_monitor && !force_monitor) { ++ wiphy_warn(wiphy, ++ "Monitor mode support is buggy in this firmware, not enabling\n"); ++ err = -EINVAL; ++ } ++ break; ++ ++ default: ++ err = -EINVAL; ++ } ++ ++ if (!err) { ++ priv->iw_mode = type; ++ set_port_type(priv); ++ err = orinoco_commit(priv); ++ } ++ ++ orinoco_unlock(priv, &lock); ++ ++ return err; ++} ++ ++static int orinoco_scan(struct wiphy *wiphy, ++ struct cfg80211_scan_request *request) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ int err; ++ ++ if (!request) ++ return -EINVAL; ++ ++ if (priv->scan_request && priv->scan_request != request) ++ return -EBUSY; ++ ++ priv->scan_request = request; ++ ++ err = orinoco_hw_trigger_scan(priv, request->ssids); ++ /* On error the we aren't processing the request */ ++ if (err) ++ priv->scan_request = NULL; ++ ++ return err; ++} ++ ++static int orinoco_set_monitor_channel(struct wiphy *wiphy, ++ struct net_device *dev, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ int err = 0; ++ unsigned long flags; ++ int channel; ++ ++ if (!chandef->chan) ++ return -EINVAL; ++ ++ if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) ++ return -EINVAL; ++ ++ if (chandef->chan->band != NL80211_BAND_2GHZ) ++ return -EINVAL; ++ ++ channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); ++ ++ if ((channel < 1) || (channel > NUM_CHANNELS) || ++ !(priv->channel_mask & (1 << (channel - 1)))) ++ return -EINVAL; ++ ++ if (orinoco_lock(priv, &flags) != 0) ++ return -EBUSY; ++ ++ priv->channel = channel; ++ if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { ++ /* Fast channel change - no commit if successful */ ++ struct hermes *hw = &priv->hw; ++ err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | ++ HERMES_TEST_SET_CHANNEL, ++ channel, NULL); ++ } ++ orinoco_unlock(priv, &flags); ++ ++ return err; ++} ++ ++static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) ++{ ++ struct orinoco_private *priv = wiphy_priv(wiphy); ++ int frag_value = -1; ++ int rts_value = -1; ++ int err = 0; ++ ++ if (changed & WIPHY_PARAM_RETRY_SHORT) { ++ /* Setting short retry not supported */ ++ err = -EINVAL; ++ } ++ ++ if (changed & WIPHY_PARAM_RETRY_LONG) { ++ /* Setting long retry not supported */ ++ err = -EINVAL; ++ } ++ ++ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { ++ /* Set fragmentation */ ++ if (priv->has_mwo) { ++ if (wiphy->frag_threshold == -1) ++ frag_value = 0; ++ else { ++ printk(KERN_WARNING "%s: Fixed fragmentation " ++ "is not supported on this firmware. " ++ "Using MWO robust instead.\n", ++ priv->ndev->name); ++ frag_value = 1; ++ } ++ } else { ++ if (wiphy->frag_threshold == -1) ++ frag_value = 2346; ++ else if ((wiphy->frag_threshold < 257) || ++ (wiphy->frag_threshold > 2347)) ++ err = -EINVAL; ++ else ++ /* cfg80211 value is 257-2347 (odd only) ++ * orinoco rid has range 256-2346 (even only) */ ++ frag_value = wiphy->frag_threshold & ~0x1; ++ } ++ } ++ ++ if (changed & WIPHY_PARAM_RTS_THRESHOLD) { ++ /* Set RTS. ++ * ++ * Prism documentation suggests default of 2432, ++ * and a range of 0-3000. ++ * ++ * Current implementation uses 2347 as the default and ++ * the upper limit. ++ */ ++ ++ if (wiphy->rts_threshold == -1) ++ rts_value = 2347; ++ else if (wiphy->rts_threshold > 2347) ++ err = -EINVAL; ++ else ++ rts_value = wiphy->rts_threshold; ++ } ++ ++ if (!err) { ++ unsigned long flags; ++ ++ if (orinoco_lock(priv, &flags) != 0) ++ return -EBUSY; ++ ++ if (frag_value >= 0) { ++ if (priv->has_mwo) ++ priv->mwo_robust = frag_value; ++ else ++ priv->frag_thresh = frag_value; ++ } ++ if (rts_value >= 0) ++ priv->rts_thresh = rts_value; ++ ++ err = orinoco_commit(priv); ++ ++ orinoco_unlock(priv, &flags); ++ } ++ ++ return err; ++} ++ ++const struct cfg80211_ops orinoco_cfg_ops = { ++ .change_virtual_intf = orinoco_change_vif, ++ .set_monitor_channel = orinoco_set_monitor_channel, ++ .scan = orinoco_scan, ++ .set_wiphy_params = orinoco_set_wiphy_params, ++}; +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.h linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.h +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.h 2025-08-03 15:01:59.313131673 +0200 +@@ -0,0 +1,15 @@ ++/* cfg80211 support. ++ * ++ * See copyright notice in main.c ++ */ ++#ifndef ORINOCO_CFG_H ++#define ORINOCO_CFG_H ++ ++#include <net/cfg80211.h> ++ ++extern const struct cfg80211_ops orinoco_cfg_ops; ++ ++void orinoco_wiphy_init(struct wiphy *wiphy); ++int orinoco_wiphy_register(struct wiphy *wiphy); ++ ++#endif /* ORINOCO_CFG_H */ +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/fw.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/fw.c +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/fw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/fw.c 2025-08-03 15:01:59.313131673 +0200 +@@ -0,0 +1,387 @@ ++/* Firmware file reading and download helpers ++ * ++ * See copyright notice in main.c ++ */ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/firmware.h> ++#include <linux/device.h> ++#include <linux/module.h> ++ ++#include "hermes.h" ++#include "hermes_dld.h" ++#include "orinoco.h" ++ ++#include "fw.h" ++ ++/* End markers (for Symbol firmware only) */ ++#define TEXT_END 0x1A /* End of text header */ ++ ++struct fw_info { ++ char *pri_fw; ++ char *sta_fw; ++ char *ap_fw; ++ u32 pda_addr; ++ u16 pda_size; ++}; ++ ++static const struct fw_info orinoco_fw[] = { ++ { NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, ++ { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, ++ { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 } ++}; ++MODULE_FIRMWARE("agere_sta_fw.bin"); ++MODULE_FIRMWARE("agere_ap_fw.bin"); ++MODULE_FIRMWARE("prism_sta_fw.bin"); ++MODULE_FIRMWARE("prism_ap_fw.bin"); ++MODULE_FIRMWARE("symbol_sp24t_prim_fw"); ++MODULE_FIRMWARE("symbol_sp24t_sec_fw"); ++ ++/* Structure used to access fields in FW ++ * Make sure LE decoding macros are used ++ */ ++struct orinoco_fw_header { ++ char hdr_vers[6]; /* ASCII string for header version */ ++ __le16 headersize; /* Total length of header */ ++ __le32 entry_point; /* NIC entry point */ ++ __le32 blocks; /* Number of blocks to program */ ++ __le32 block_offset; /* Offset of block data from eof header */ ++ __le32 pdr_offset; /* Offset to PDR data from eof header */ ++ __le32 pri_offset; /* Offset to primary plug data */ ++ __le32 compat_offset; /* Offset to compatibility data*/ ++ char signature[]; /* FW signature length headersize-20 */ ++} __packed; ++ ++/* Check the range of various header entries. Return a pointer to a ++ * description of the problem, or NULL if everything checks out. */ ++static const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len) ++{ ++ u16 hdrsize; ++ ++ if (len < sizeof(*hdr)) ++ return "image too small"; ++ if (memcmp(hdr->hdr_vers, "HFW", 3) != 0) ++ return "format not recognised"; ++ ++ hdrsize = le16_to_cpu(hdr->headersize); ++ if (hdrsize > len) ++ return "bad headersize"; ++ if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len) ++ return "bad block offset"; ++ if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len) ++ return "bad PDR offset"; ++ if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len) ++ return "bad PRI offset"; ++ if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len) ++ return "bad compat offset"; ++ ++ /* TODO: consider adding a checksum or CRC to the firmware format */ ++ return NULL; ++} ++ ++#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) ++static inline const struct firmware * ++orinoco_cached_fw_get(struct orinoco_private *priv, bool primary) ++{ ++ if (primary) ++ return priv->cached_pri_fw; ++ else ++ return priv->cached_fw; ++} ++#else ++#define orinoco_cached_fw_get(priv, primary) (NULL) ++#endif ++ ++/* Download either STA or AP firmware into the card. */ ++static int ++orinoco_dl_firmware(struct orinoco_private *priv, ++ const struct fw_info *fw, ++ int ap) ++{ ++ /* Plug Data Area (PDA) */ ++ __le16 *pda; ++ ++ struct hermes *hw = &priv->hw; ++ const struct firmware *fw_entry; ++ const struct orinoco_fw_header *hdr; ++ const unsigned char *first_block; ++ const void *end; ++ const char *firmware; ++ const char *fw_err; ++ struct device *dev = priv->dev; ++ int err = 0; ++ ++ pda = kzalloc(fw->pda_size, GFP_KERNEL); ++ if (!pda) ++ return -ENOMEM; ++ ++ if (ap) ++ firmware = fw->ap_fw; ++ else ++ firmware = fw->sta_fw; ++ ++ dev_dbg(dev, "Attempting to download firmware %s\n", firmware); ++ ++ /* Read current plug data */ ++ err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); ++ dev_dbg(dev, "Read PDA returned %d\n", err); ++ if (err) ++ goto free; ++ ++ if (!orinoco_cached_fw_get(priv, false)) { ++ err = request_firmware(&fw_entry, firmware, priv->dev); ++ ++ if (err) { ++ dev_err(dev, "Cannot find firmware %s\n", firmware); ++ err = -ENOENT; ++ goto free; ++ } ++ } else ++ fw_entry = orinoco_cached_fw_get(priv, false); ++ ++ hdr = (const struct orinoco_fw_header *) fw_entry->data; ++ ++ fw_err = validate_fw(hdr, fw_entry->size); ++ if (fw_err) { ++ dev_warn(dev, "Invalid firmware image detected (%s). " ++ "Aborting download\n", fw_err); ++ err = -EINVAL; ++ goto abort; ++ } ++ ++ /* Enable aux port to allow programming */ ++ err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point)); ++ dev_dbg(dev, "Program init returned %d\n", err); ++ if (err != 0) ++ goto abort; ++ ++ /* Program data */ ++ first_block = (fw_entry->data + ++ le16_to_cpu(hdr->headersize) + ++ le32_to_cpu(hdr->block_offset)); ++ end = fw_entry->data + fw_entry->size; ++ ++ err = hermes_program(hw, first_block, end); ++ dev_dbg(dev, "Program returned %d\n", err); ++ if (err != 0) ++ goto abort; ++ ++ /* Update production data */ ++ first_block = (fw_entry->data + ++ le16_to_cpu(hdr->headersize) + ++ le32_to_cpu(hdr->pdr_offset)); ++ ++ err = hermes_apply_pda_with_defaults(hw, first_block, end, pda, ++ &pda[fw->pda_size / sizeof(*pda)]); ++ dev_dbg(dev, "Apply PDA returned %d\n", err); ++ if (err) ++ goto abort; ++ ++ /* Tell card we've finished */ ++ err = hw->ops->program_end(hw); ++ dev_dbg(dev, "Program end returned %d\n", err); ++ if (err != 0) ++ goto abort; ++ ++ /* Check if we're running */ ++ dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw)); ++ ++abort: ++ /* If we requested the firmware, release it. */ ++ if (!orinoco_cached_fw_get(priv, false)) ++ release_firmware(fw_entry); ++ ++free: ++ kfree(pda); ++ return err; ++} ++ ++/* ++ * Process a firmware image - stop the card, load the firmware, reset ++ * the card and make sure it responds. For the secondary firmware take ++ * care of the PDA - read it and then write it on top of the firmware. ++ */ ++static int ++symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, ++ const unsigned char *image, const void *end, ++ int secondary) ++{ ++ struct hermes *hw = &priv->hw; ++ int ret = 0; ++ const unsigned char *ptr; ++ const unsigned char *first_block; ++ ++ /* Plug Data Area (PDA) */ ++ __le16 *pda = NULL; ++ ++ /* Binary block begins after the 0x1A marker */ ++ ptr = image; ++ while (*ptr++ != TEXT_END); ++ first_block = ptr; ++ ++ /* Read the PDA from EEPROM */ ++ if (secondary) { ++ pda = kzalloc(fw->pda_size, GFP_KERNEL); ++ if (!pda) ++ return -ENOMEM; ++ ++ ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); ++ if (ret) ++ goto free; ++ } ++ ++ /* Stop the firmware, so that it can be safely rewritten */ ++ if (priv->stop_fw) { ++ ret = priv->stop_fw(priv, 1); ++ if (ret) ++ goto free; ++ } ++ ++ /* Program the adapter with new firmware */ ++ ret = hermes_program(hw, first_block, end); ++ if (ret) ++ goto free; ++ ++ /* Write the PDA to the adapter */ ++ if (secondary) { ++ size_t len = hermes_blocks_length(first_block, end); ++ ptr = first_block + len; ++ ret = hermes_apply_pda(hw, ptr, end, pda, ++ &pda[fw->pda_size / sizeof(*pda)]); ++ kfree(pda); ++ if (ret) ++ return ret; ++ } ++ ++ /* Run the firmware */ ++ if (priv->stop_fw) { ++ ret = priv->stop_fw(priv, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* Reset hermes chip and make sure it responds */ ++ ret = hw->ops->init(hw); ++ ++ /* hermes_reset() should return 0 with the secondary firmware */ ++ if (secondary && ret != 0) ++ return -ENODEV; ++ ++ /* And this should work with any firmware */ ++ if (!hermes_present(hw)) ++ return -ENODEV; ++ ++ return 0; ++ ++free: ++ kfree(pda); ++ return ret; ++} ++ ++ ++/* ++ * Download the firmware into the card, this also does a PCMCIA soft ++ * reset on the card, to make sure it's in a sane state. ++ */ ++static int ++symbol_dl_firmware(struct orinoco_private *priv, ++ const struct fw_info *fw) ++{ ++ struct device *dev = priv->dev; ++ int ret; ++ const struct firmware *fw_entry; ++ ++ if (!orinoco_cached_fw_get(priv, true)) { ++ if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) { ++ dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw); ++ return -ENOENT; ++ } ++ } else ++ fw_entry = orinoco_cached_fw_get(priv, true); ++ ++ /* Load primary firmware */ ++ ret = symbol_dl_image(priv, fw, fw_entry->data, ++ fw_entry->data + fw_entry->size, 0); ++ ++ if (!orinoco_cached_fw_get(priv, true)) ++ release_firmware(fw_entry); ++ if (ret) { ++ dev_err(dev, "Primary firmware download failed\n"); ++ return ret; ++ } ++ ++ if (!orinoco_cached_fw_get(priv, false)) { ++ if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) { ++ dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw); ++ return -ENOENT; ++ } ++ } else ++ fw_entry = orinoco_cached_fw_get(priv, false); ++ ++ /* Load secondary firmware */ ++ ret = symbol_dl_image(priv, fw, fw_entry->data, ++ fw_entry->data + fw_entry->size, 1); ++ if (!orinoco_cached_fw_get(priv, false)) ++ release_firmware(fw_entry); ++ if (ret) ++ dev_err(dev, "Secondary firmware download failed\n"); ++ ++ return ret; ++} ++ ++int orinoco_download(struct orinoco_private *priv) ++{ ++ int err = 0; ++ /* Reload firmware */ ++ switch (priv->firmware_type) { ++ case FIRMWARE_TYPE_AGERE: ++ /* case FIRMWARE_TYPE_INTERSIL: */ ++ err = orinoco_dl_firmware(priv, ++ &orinoco_fw[priv->firmware_type], 0); ++ break; ++ ++ case FIRMWARE_TYPE_SYMBOL: ++ err = symbol_dl_firmware(priv, ++ &orinoco_fw[priv->firmware_type]); ++ break; ++ case FIRMWARE_TYPE_INTERSIL: ++ break; ++ } ++ /* TODO: if we fail we probably need to reinitialise ++ * the driver */ ++ ++ return err; ++} ++ ++#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) ++void orinoco_cache_fw(struct orinoco_private *priv, int ap) ++{ ++ const struct firmware *fw_entry = NULL; ++ const char *pri_fw; ++ const char *fw; ++ ++ pri_fw = orinoco_fw[priv->firmware_type].pri_fw; ++ if (ap) ++ fw = orinoco_fw[priv->firmware_type].ap_fw; ++ else ++ fw = orinoco_fw[priv->firmware_type].sta_fw; ++ ++ if (pri_fw) { ++ if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0) ++ priv->cached_pri_fw = fw_entry; ++ } ++ ++ if (fw) { ++ if (request_firmware(&fw_entry, fw, priv->dev) == 0) ++ priv->cached_fw = fw_entry; ++ } ++} ++ ++void orinoco_uncache_fw(struct orinoco_private *priv) ++{ ++ release_firmware(priv->cached_pri_fw); ++ release_firmware(priv->cached_fw); ++ priv->cached_pri_fw = NULL; ++ priv->cached_fw = NULL; ++} ++#endif +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/fw.h linux-6.15.6/drivers/net/wireless/intersil/orinoco/fw.h +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/fw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/fw.h 2025-08-03 15:01:59.313131673 +0200 +@@ -0,0 +1,21 @@ ++/* Firmware file reading and download helpers ++ * ++ * See copyright notice in main.c ++ */ ++#ifndef _ORINOCO_FW_H_ ++#define _ORINOCO_FW_H_ ++ ++/* Forward declations */ ++struct orinoco_private; ++ ++int orinoco_download(struct orinoco_private *priv); ++ ++#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) ++void orinoco_cache_fw(struct orinoco_private *priv, int ap); ++void orinoco_uncache_fw(struct orinoco_private *priv); ++#else ++#define orinoco_cache_fw(priv, ap) do { } while (0) ++#define orinoco_uncache_fw(priv) do { } while (0) ++#endif ++ ++#endif /* _ORINOCO_FW_H_ */ +diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/hermes.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/hermes.c +--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/hermes.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/hermes.c 2025-08-03 22:42:47.279513981 +0200 +@@ -0,0 +1,778 @@ ++/* hermes.c ++ * ++ * Driver core for the "Hermes" wireless MAC controller, as used in ++ * the Lucent Orinoco and Cabletron RoamAbout cards. It should also ++ * work on the hfa3841 and hfa3842 MAC controller chips used in the ++ * Prism II chipsets. ++ * ++ * This is not a complete driver, just low-level access routines for ++ * the MAC controller itself. ++ * ++ * Based on the prism2 driver from Absolute Value Systems' linux-wlan ++ * project, the Linux wvlan_cs driver, Lucent's HCF-Light ++ * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no ++ * particular order). ++ * ++ * Copyright (C) 2000, David Gibson, Linuxcare Australia. ++ * (C) Copyright David Gibson, IBM Corp. 2001-2003. ++ * ++ * The contents of this file are subject to the Mozilla Public License ++ * Version 1.1 (the "License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License ++ * at http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See ++ * the License for the specific language governing rights and ++ * limitations under the License. ++ * ++ * Alternatively, the contents of this file may be used under the ++ * terms of the GNU General Public License version 2 (the "GPL"), in ++ * which case the provisions of the GPL are applicable instead of the ++ * above. If you wish to allow the use of your version of this file ++ * only under the terms of the GPL and not to allow others to use your ++ * version of this file under the MPL, indicate your decision by ++ * deleting the provisions above and replace them with the notice and ++ * other provisions required by the GPL. If you do not delete the ++ * provisions above, a recipient may use your version of this file ++ * under either the MPL or the GPL. ++ */ ++ ++#include <linux/net.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++ ++#include "hermes.h" ++ ++/* These are maximum timeouts. Most often, card wil react much faster */ ++#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ ++#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ ++#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ ++#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ ++ ++/* ++ * AUX port access. To unlock the AUX port write the access keys to the ++ * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL ++ * register. Then read it and make sure it's HERMES_AUX_ENABLED. ++ */ ++#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ ++#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ ++#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ ++#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ ++ ++#define HERMES_AUX_PW0 0xFE01 ++#define HERMES_AUX_PW1 0xDC23 ++#define HERMES_AUX_PW2 0xBA45 ++ ++/* HERMES_CMD_DOWNLD */ ++#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) ++#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) ++#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) ++#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) ++ ++/* ++ * Debugging helpers ++ */ ++ ++#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \ ++ printk(stuff); } while (0) ++ ++#undef HERMES_DEBUG ++#ifdef HERMES_DEBUG ++ ++#define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff) ++ ++#else /* ! HERMES_DEBUG */ ++ ++#define DEBUG(lvl, stuff...) do { } while (0) ++ ++#endif /* ! HERMES_DEBUG */ ++ ++static const struct hermes_ops hermes_ops_local; ++ ++/* ++ * Internal functions ++ */ ++ ++/* Issue a command to the chip. Waiting for it to complete is the caller's ++ problem. ++ ++ Returns -EBUSY if the command register is busy, 0 on success. ++ ++ Callable from any context. ++*/ ++static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0, ++ |