STRUCT ETHTOOL_OPS(9)

πŸ“¦ version 7.3.1, in nixpkgs unstable

πŸ’– struct ethtool_ops πŸ’–

okay bestie, welcome to section 9 of the Kernel API Manual β€” this is the page for struct ethtool_ops, and it is literally giving β€œoptional netdev operations” main character energy. part 1, no cap, let’s get into it 🫢✨


🌸 NAME 🌸

struct ethtool_ops β€” optional netdev operations πŸ’…


✨ SYNOPSIS ✨

here’s the whole struct, bestie β€” every field, exactly as it is, no edits, no vibes-based rewriting πŸ”₯

       struct ethtool_ops {
           u32 supported_input_xfrm:8;
           u32 cap_link_lanes_supported:1;
           u32 rxfh_per_ctx_fields:1;
           u32 rxfh_per_ctx_key:1;
           u32 cap_rss_rxnfc_adds:1;
           u32 rxfh_indir_space;
           u16 rxfh_key_space;
           u16 rxfh_priv_size;
           u32 rxfh_max_num_contexts;
           u32 supported_coalesce_params;
           u32 supported_ring_params;
           u32 supported_hwtstamp_qualifiers;
           u32 op_needs_rtnl;
           void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
           int (*get_regs_len)(struct net_device *);
           void (*get_regs)(struct net_device *, struct ethtool_regs *, void
       *);
           void (*get_wol)(struct net_device *, struct ethtool_wolinfo *);
           int (*set_wol)(struct net_device *, struct ethtool_wolinfo *);
           u32 (*get_msglevel)(struct net_device *);
           void (*set_msglevel)(struct net_device *, u32);
           int (*nway_reset)(struct net_device *);
           u32 (*get_link)(struct net_device *);
           int (*get_link_ext_state)(struct net_device *, struct
       ethtool_link_ext_state_info *);
           void (*get_link_ext_stats)(struct net_device *dev, struct
       ethtool_link_ext_stats *stats);
           int (*get_eeprom_len)(struct net_device *);
           int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8
       *);
           int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8
       *);
           int (*get_coalesce)(struct net_device *, struct ethtool_coalesce *,
       struct kernel_ethtool_coalesce *, struct netlink_ext_ack *);
           int (*set_coalesce)(struct net_device *, struct ethtool_coalesce *,
       struct kernel_ethtool_coalesce *, struct netlink_ext_ack *);
           void (*get_ringparam)(struct net_device *, struct ethtool_ringparam
       *, struct kernel_ethtool_ringparam *, struct netlink_ext_ack *);
           int (*set_ringparam)(struct net_device *, struct ethtool_ringparam
       *, struct kernel_ethtool_ringparam *, struct netlink_ext_ack *);
           void (*get_pause_stats)(struct net_device *dev, struct
       ethtool_pause_stats *pause_stats);
           void (*get_pauseparam)(struct net_device *, struct
       ethtool_pauseparam*);
           int (*set_pauseparam)(struct net_device *, struct
       ethtool_pauseparam*);
           void (*self_test)(struct net_device *, struct ethtool_test *, u64
       *);
           void (*get_strings)(struct net_device *, u32 stringset, u8 *);
           int (*set_phys_id)(struct net_device *, enum ethtool_phys_id_state);
           void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats
       *, u64 *);
           int (*begin)(struct net_device *);
           void (*complete)(struct net_device *);
           u32 (*get_priv_flags)(struct net_device *);
           int (*set_priv_flags)(struct net_device *, u32);
           int (*get_sset_count)(struct net_device *, int);
           int (*get_rxnfc)(struct net_device *, struct ethtool_rxnfc *, u32
       *rule_locs);
           int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *);
           int (*flash_device)(struct net_device *, struct ethtool_flash *);
           int (*reset)(struct net_device *, u32 *);
           u32 (*get_rx_ring_count)(struct net_device *dev);
           u32 (*get_rxfh_key_size)(struct net_device *);
           u32 (*get_rxfh_indir_size)(struct net_device *);
           int (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *);
           int (*set_rxfh)(struct net_device *, struct ethtool_rxfh_param *,
       struct netlink_ext_ack *extack);
           int (*get_rxfh_fields)(struct net_device *, struct
       ethtool_rxfh_fields *);
           int (*set_rxfh_fields)(struct net_device *, const struct
       ethtool_rxfh_fields *, struct netlink_ext_ack *extack);
           int (*create_rxfh_context)(struct net_device *, struct
       ethtool_rxfh_context *ctx, const struct ethtool_rxfh_param *rxfh, struct
       netlink_ext_ack *extack);
           int (*modify_rxfh_context)(struct net_device *, struct
       ethtool_rxfh_context *ctx, const struct ethtool_rxfh_param *rxfh, struct
       netlink_ext_ack *extack);
           int (*remove_rxfh_context)(struct net_device *, struct
       ethtool_rxfh_context *ctx, u32 rss_context, struct netlink_ext_ack
       *extack);
           void (*get_channels)(struct net_device *, struct ethtool_channels
       *);
           int (*set_channels)(struct net_device *, struct ethtool_channels *);
           int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
           int (*get_dump_data)(struct net_device *, struct ethtool_dump *,
       void *);
           int (*set_dump)(struct net_device *, struct ethtool_dump *);
           int (*get_ts_info)(struct net_device *, struct
       kernel_ethtool_ts_info *);
           void (*get_ts_stats)(struct net_device *dev, struct ethtool_ts_stats
       *ts_stats);
           int (*get_module_info)(struct net_device *, struct ethtool_modinfo
       *);
           int (*get_module_eeprom)(struct net_device *, struct ethtool_eeprom
       *, u8 *);
           int (*get_eee)(struct net_device *dev, struct ethtool_keee *eee);
           int (*set_eee)(struct net_device *dev, struct ethtool_keee *eee);
           int (*get_tunable)(struct net_device *, const struct ethtool_tunable
       *, void *);
           int (*set_tunable)(struct net_device *, const struct ethtool_tunable
       *, const void *);
           int (*get_per_queue_coalesce)(struct net_device *, u32, struct
       ethtool_coalesce *);
           int (*set_per_queue_coalesce)(struct net_device *, u32, struct
       ethtool_coalesce *);
           int (*get_link_ksettings)(struct net_device *, struct
       ethtool_link_ksettings *);
           int (*set_link_ksettings)(struct net_device *, const struct
       ethtool_link_ksettings *);
           void (*get_fec_stats)(struct net_device *dev, struct
       ethtool_fec_stats *fec_stats, struct ethtool_fec_hist *hist);
           int (*get_fecparam)(struct net_device *, struct ethtool_fecparam *);
           int (*set_fecparam)(struct net_device *, struct ethtool_fecparam *);
           void (*get_ethtool_phy_stats)(struct net_device *, struct
       ethtool_stats *, u64 *);
           int (*get_phy_tunable)(struct net_device *, const struct
       ethtool_tunable *, void *);
           int (*set_phy_tunable)(struct net_device *, const struct
       ethtool_tunable *, const void *);
           int (*get_module_eeprom_by_page)(struct net_device *dev, const
       struct ethtool_module_eeprom *page, struct netlink_ext_ack *extack);
           int (*set_module_eeprom_by_page)(struct net_device *dev, const
       struct ethtool_module_eeprom *page, struct netlink_ext_ack *extack);
           void (*get_eth_phy_stats)(struct net_device *dev, struct
       ethtool_eth_phy_stats *phy_stats);
           void (*get_eth_mac_stats)(struct net_device *dev, struct
       ethtool_eth_mac_stats *mac_stats);
           void (*get_eth_ctrl_stats)(struct net_device *dev, struct
       ethtool_eth_ctrl_stats *ctrl_stats);
           void (*get_rmon_stats)(struct net_device *dev, struct
       ethtool_rmon_stats *rmon_stats, const struct ethtool_rmon_hist_range
       **ranges);
           int (*get_module_power_mode)(struct net_device *dev, struct
       ethtool_module_power_mode_params *params, struct netlink_ext_ack
       *extack);
           int (*set_module_power_mode)(struct net_device *dev, const struct
       ethtool_module_power_mode_params *params, struct netlink_ext_ack
       *extack);
           int (*get_mm)(struct net_device *dev, struct ethtool_mm_state
       *state);
           int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,
       struct netlink_ext_ack *extack);
           void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats
       *stats);
        };

🫢 Members 🫢

okay so here’s the member-by-member tea, bestie β€” all the details are staying, we’re just making it cute 🌸

  • supported_input_xfrm β€” supported types of input xfrm from RXH_XFRM_*. ✨
  • cap_link_lanes_supported β€” indicates if the driver supports lanes parameter. πŸ›£οΈ
  • rxfh_per_ctx_fields β€” device supports selecting different header fields for Rx hash calculation and RSS for each additional context. πŸŽ€
  • rxfh_per_ctx_key β€” device supports setting different RSS key for each additional context. Netlink API should report hfunc, key, and input_xfrm for every context, not just context 0. πŸ”‘
  • cap_rss_rxnfc_adds β€” device supports nonzero ring_cookie in filters with FLOW_RSS flag; the queue ID from the filter is added to the value from the indirection table to determine the delivery queue. πŸŒ€
  • rxfh_indir_space β€” max size of RSS indirection tables, if indirection table size as returned by get_rxfh_indir_size may change during lifetime of the device. Leave as 0 if the table size is constant. πŸ“
  • rxfh_key_space β€” same as rxfh_indir_space, but for the key. πŸ”
  • rxfh_priv_size β€” size of the driver private data area the core should allocate for an RSS context (in struct ethtool_rxfh_context). 🧺
  • rxfh_max_num_contexts β€” maximum (exclusive) supported RSS context ID. If this is zero then the core may choose any (nonzero) ID, otherwise the core will only use IDs strictly less than this value, as the rss_context argument to create_rxfh_context and friends. 🚦
  • supported_coalesce_params β€” supported types of interrupt coalescing. ⚑
  • supported_ring_params β€” supported ring params. πŸ’
  • supported_hwtstamp_qualifiers β€” bitfield of supported hwtstamp qualifier. ⏱️
  • op_needs_rtnl β€” mask of ETHTOOL_OP_NEEDS_RTNL_* bits. For use with ops-locked drivers (ignored otherwise). Selects which ethtool callbacks driver needs to still be executed under rtnl_lock (in addition to the netdev instance lock). The following commonly used core APIs currently require rtnl_lock (this list may not be exhaustive):
    • phylink helpers (note that phydev is currently unsupported!)
    • netdev_update_features
    • netif_set_real_num_tx_queues
    • ethtool_op_get_link (syncs link watch under rtnl_lock)
    • netif_open / netif_close (used by self_test)
  • get_drvinfo β€” Report driver/device information. Modern drivers no longer have to implement this callback. Most fields are correctly filled in by the core using system information, or populated using other driver operations. πŸ“‹
  • get_regs_len β€” Get buffer length required for get_regs. πŸ“
  • get_regs β€” Get device registers. 🧾
  • get_wol β€” Report whether Wake-on-Lan is enabled. πŸŒ™
  • set_wol β€” Turn Wake-on-Lan on or off. Returns a negative error code or zero. πŸ”Œ
  • get_msglevel β€” Report driver message level. This should be the value of the msg_enable field used by netif logging functions. πŸ—£οΈ
  • set_msglevel β€” Set driver message level. 🎚️
  • nway_reset β€” Restart autonegotiation. Returns a negative error code or zero. πŸ”
  • get_link β€” tells you whether the physical link is actually up πŸ’–. It only ever gets called if the netdev is up, so no worries about it firing at weird times. Ngl you should usually just point this at ethtool_op_get_link, which does the whole thing via netif_carrier_ok. slay, less work for you.
  • get_link_ext_state β€” reports the link's extended state ✨. You set link_ext_state and link_ext_substate β€” and heads up, a link_ext_substate of 0 means the substate is unknown, and in that case do not attach the ext_substate attribute to the netlink message. If both link_ext_state and link_ext_substate are unknown, return -ENODATA. And if this op just isn't implemented at all, then link_ext_state and link_ext_substate simply won't be sent to userspace. iykyk.
  • get_link_ext_stats β€” reads the extra link-related counters πŸ¦‹. cute lil bonus stats.
  • get_eeprom_len β€” reads the range of EEPROM addresses so the kernel can validate get_eeprom and set_eeprom requests πŸŽ€. Returns 0 if the device doesn't support EEPROM access at all.
  • get_eeprom β€” reads data straight out of the device EEPROM πŸ’…. Fill in the magic field, bestie. You don't need to check len for zero or wraparound β€” the kernel's got you. Fill the data argument with the EEPROM values from offset to offset + len, then update len to the amount actually read. Returns an error or zero.
  • set_eeprom β€” writes data to the device EEPROM πŸ”₯. This one should validate the magic field (yes, really). Again, no need to check len for zero or wraparound. Update len to the amount written. Returns an error or zero.
  • get_coalesce β€” gets the interrupt coalescing parameters ⭐. Returns a negative error code or zero, the usual deal.
  • set_coalesce β€” sets the interrupt coalescing parameters πŸ’œ. The coalescing types you actually support should be set in supported_coalesce_params. Returns a negative error code or zero.
  • get_ringparam β€” reports ring sizes 🌸. that's it, that's the whole vibe.
  • set_ringparam β€” sets ring sizes 🫢. Returns a negative error code or zero.
  • get_pause_stats β€” reports pause frame statistics 🌈. Important: drivers must not zero statistics they don't report β€” the stats structure is initialized to ETHTOOL_STAT_NOT_SET, which is the signal that the driver doesn't report those statistics. Don't be out here lying with zeros, fr fr.
  • get_pauseparam β€” reports pause parameters πŸ’«.
  • set_pauseparam β€” sets pause parameters πŸ’–. Returns a negative error code or zero.
  • self_test β€” runs the specified self-tests πŸ”₯. go little device go.
  • get_strings β€” returns a set of strings that describe the requested objects ✨.
  • set_phys_id β€” identifies the physical devices, like by flashing an LED attached to it πŸ’‘. The implementation may update the indicator asynchronously or synchronously, but either way it must return quickly. Here's the whole choreography: it's initially called with the argument ETHTOOL_ID_ACTIVE, and it must either activate asynchronous updates and return zero, return a negative error, or return a positive frequency for synchronous indication (e.g. 1 for one on/off cycle per second). If it returns a frequency, then it'll get called again at intervals with the argument ETHTOOL_ID_ON or ETHTOOL_ID_OFF, and it should set the state of the indicator accordingly. Finally, it's called with the argument ETHTOOL_ID_INACTIVE and must deactivate the indicator. Returns a negative error code or zero. main character LED energy, honestly.
  • get_ethtool_stats β€” returns extended statistics about the device πŸŽ€. This is only useful if the device maintains statistics that aren't already included in struct rtnl_link_stats64.
  • begin β€” the function called before any other operation πŸ¦‹. Returns a negative error code or zero.
  • complete β€” the function called after any other operation except begin πŸ’…. And yes, it will be called even if the other operation failed. cleanup queen.
  • get_priv_flags β€” reports driver-specific feature flags ⭐.
  • set_priv_flags β€” sets driver-specific feature flags πŸ’œ. Returns a negative error code or zero.
  • get_sset_count β€” gets the number of strings that get_strings will write 🌸.
  • get_rxnfc β€” gets the RX flow classification rules πŸ¦‹. Returns a negative error code or zero. Now pay attention, because this one's got lore: for ETHTOOL_GRXCLSRLALL, rule_cnt and the size of the arrays are user-provided, and are not guaranteed to match what the driver would have reported via ETHTOOL_GRXCLSRLCNT. Drivers must return -EMSGSIZE when rule_cnt is too small. rule_locs is NULL when rule_cnt is zero. On success, drivers must set rule_cnt to the number of locations they filled in, and the core copies out exactly that many. no cap, read it twice.
  • set_rxnfc β€” sets the RX flow classification rules πŸ’–. Returns a negative error code or zero.
  • flash_device β€” writes a firmware image to the device's flash memory πŸ”₯. Returns a negative error code or zero.
  • reset β€” resets (part of) the device, as specified by a bitmask of flags from enum ethtool_reset_flags ✨. Returns a negative error code or zero.
  • get_rx_ring_count β€” returns the number of RX rings πŸŽ€.
  • get_rxfh_key_size β€” gets the size of the RX flow hash key πŸ‘‘. Returns zero if it's not supported for this specific device.
  • get_rxfh_indir_size β€” gets the size of the RX flow hash indirection table πŸ’…. Returns zero if it's not supported for this specific device.
  • get_rxfh β€” gets the contents of the RX flow hash indirection table, hash key and/or hash function 🌈. Returns a negative error code or zero.
  • set_rxfh β€” sets the contents of the RX flow hash indirection table, hash key, and/or hash function 🫢. Arguments which are set to NULL or zero will remain unchanged. Returns a negative error code or zero. And an error code must be returned if at least one unsupported change was requested β€” don't silently swallow it, bestie.
  • get_rxfh_fields β€” gets the header fields used for flow hashing 🌸.
  • set_rxfh_fields β€” sets the header fields used for flow hashing πŸ’œ.
  • create_rxfh_context β€” creates a new RSS context with the specified RX flow hash indirection table, hash key, and hash function ⭐. The struct ethtool_rxfh_context for this context is passed in ctx β€” and note that as of this call, the indir table, hkey and hfunc are not yet populated. The driver doesn't need to update these; the core will do so if this op succeeds. However, if rxfh.indir is set to NULL, the driver must update the indir table in ctx with the (default or inherited) table actually in use; similarly, if rxfh.key is NULL, rxfh.hfunc is ETH_RSS_HASH_NO_CHANGE, or rxfh.input_xfrm is RXH_XFRM_NO_CHANGE, the driver should update the corresponding information in ctx. If the driver provides this method, it must also provide modify_rxfh_context and remove_rxfh_context. Returns a negative error code or zero. it's giving package deal, fr fr.
  • modify_rxfh_context β€” reconfigures the specified RSS context πŸ’«. Lets you set the contents of the RX flow hash indirection table, hash key, and/or hash function associated with the given context. Parameters which are set to NULL or zero will remain unchanged. The struct ethtool_rxfh_context for this context is passed in ctx β€” and note that it will still contain the old settings. The driver doesn't need to update these; the core will do so if this op succeeds. Returns a negative error code or zero. An error code must be returned if at least one unsupported change was requested. same rules as before, no skipping.
  • remove_rxfh_context β€” removes the specified RSS context πŸ’€. The struct ethtool_rxfh_context for this context is passed in ctx. Returns a negative error code or zero.
  • get_channels β€” gets the number of channels 🌸.
  • set_channels β€” sets the number of channels πŸ¦‹. Returns a negative error code or zero.
  • get_dump_flag β€” gets the dump flag indicating the current dump length, version, and flag of the device πŸŽ€.
  • get_dump_data β€” gets dump data ✨.
  • set_dump β€” sets dump specific flags to the device πŸ’….
  • get_ts_info β€” gets the time stamping and PTP hardware clock capabilities ⏰. It may be called with RCU, or rtnl, or a reference on the device. Drivers supporting transmit time stamps in software should set this to ethtool_op_get_ts_info.
  • get_ts_stats β€” queries the device hardware timestamping statistics πŸ”₯. Same rule as the pause stats: drivers must not zero statistics they don't report β€” the stats structure is initialized to ETHTOOL_STAT_NOT_SET, which indicates the driver doesn't report statistics. consistency is iconic.
  • get_module_info β€” gets the size and type of the EEPROM contained within a plug-in module 🧚.
  • get_module_eeprom β€” gets the EEPROM information from the plug-in module πŸ“.
  • get_eee β€” gets Energy-Efficient (EEE) supported and status 🌱.
  • set_eee β€” sets EEE status (enable/disable) as well as LPI timers ⚑.
  • get_tunable β€” reads the value of a driver / device tunable πŸŽ›οΈ.
  • set_tunable β€” sets the value of a driver / device tunable 🎚️.
  • get_per_queue_coalesce β€” gets interrupt coalescing parameters per queue πŸ‘‘. It must check that the given queue number is valid. If neither an RX nor a TX queue has this number, return -EINVAL. If only an RX queue or a TX queue has this number, set the inapplicable fields to ~0 and return 0. Returns a negative error code or zero. precision, bestie.
  • set_per_queue_coalesce β€” sets interrupt coalescing parameters per queue πŸ’–. It must check that the given queue number is valid. If neither an RX nor a TX queue has this number, return -EINVAL. If only an RX queue or a TX queue has this number, ignore the inapplicable fields. Supported coalescing types should be set in supported_coalesce_params. Returns a negative error code or zero.
  • get_link_ksettings β€” gets various device settings including Ethernet link settings πŸ”₯. Heads up: the cmd and link_mode_masks_nwords fields should be ignored (use __ETHTOOL_LINK_MODE_MASK_NBITS instead of the latter), and any change to them will be overwritten by the kernel. Returns a negative error code or zero. so like, don't bother touching them, it's not giving what you think it's giving πŸ™ˆ.
  • set_link_ksettings β€” sets various device settings, including Ethernet link settings. The cmd and link_mode_masks_nwords fields should be ignored (use __ETHTOOL_LINK_MODE_MASK_NBITS instead of the latter), and any change to them will be overwritten by kernel. Returns a negative error code or zero. πŸ’…
  • get_fec_stats β€” reports FEC statistics. Core will sum up per-lane stats to get the total. Drivers must not zero statistics which they don't report. The stats structure is initialized to ETHTOOL_STAT_NOT_SET, indicating the driver does not report statistics. 🫢
  • get_fecparam β€” get the network device Forward Error Correction parameters. ✨
  • set_fecparam β€” set the network device Forward Error Correction parameters. πŸŽ€
  • get_ethtool_phy_stats β€” return extended statistics about the PHY device. This is only useful if the device maintains PHY statistics and cannot use the standard PHY library helpers. πŸ¦‹
  • get_phy_tunable β€” read the value of a PHY tunable. 🌸
  • set_phy_tunable β€” set the value of a PHY tunable. 🌸
  • get_module_eeprom_by_page β€” get a region of plug-in module EEPROM data from the specified page. Returns a negative error code or the amount of bytes read. πŸ’–
  • set_module_eeprom_by_page β€” write to a region of plug-in module EEPROM, from kernel space only. Returns a negative error code or zero. πŸ”₯
  • get_eth_phy_stats β€” query some of the IEEE 802.3 PHY statistics. πŸ‘‘
  • get_eth_mac_stats β€” query some of the IEEE 802.3 MAC statistics. πŸ‘‘
  • get_eth_ctrl_stats β€” query some of the IEEE 802.3 MAC Ctrl statistics. πŸ‘‘
  • get_rmon_stats β€” query some of the RMON (RFC 2819) statistics. Set ranges to a pointer to zero-terminated array of byte ranges. 🌈
  • get_module_power_mode β€” get the power mode policy for the plug-in module used by the network device and its operational power mode, if plugged-in. ⭐
  • set_module_power_mode β€” set the power mode policy for the plug-in module used by the network device. ⭐
  • get_mm β€” query the 802.3 MAC Merge layer state. 🧚
  • set_mm β€” set the 802.3 MAC Merge layer parameters. 🧚
  • get_mm_stats β€” query the 802.3 MAC Merge layer statistics. 🧚

🌈 Description 🌈

Okay so real talk, bestie: all operations are optional β€” the function pointer may literally be set to NULL β€” and callers must take this into account. No assumptions, no cap. πŸ’€

For traditional drivers, callers hold rtnl_lock across the call. For "ops locked" drivers (see Documentation/networking/netdevices.rst), callers instead hold the netdev instance lock (netdev_lock_ops); rtnl_lock is additionally held only for callbacks for which the driver opts in via the matching ETHTOOL_OP_NEEDS_RTNL_* bit in op_needs_rtnl. πŸ”

See the structures used by these operations for further documentation. Note that for all operations using a structure ending with a zero-length array, the array is allocated separately in the kernel and is passed to the driver as an additional parameter. 🧠✨

See struct net_device and struct net_device_ops for documentation of the generic netdev features interface. 🫢


πŸŽ€ SEE ALSO πŸŽ€

The whole extended friendship circle, no skips:


That's the whole page, bestie πŸ’– source lives at ./include/linux, the version date is October 2026, and this iconic little manual is struct ethtool_ops(9). Go forth and slay your driver ops πŸ’…βœ¨

πŸ“¦ where this page comes from