#include <linux/bug.h>
 #include <linux/device.h>
-#include <linux/ethtool.h>
 #include <linux/firewire.h>
 #include <linux/firewire-constants.h>
 #include <linux/highmem.h>
        return 0;
 }
 
-static void fwnet_get_drvinfo(struct net_device *net,
-                             struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, KBUILD_MODNAME);
-       strcpy(info->bus_info, "ieee1394");
-}
-
-static const struct ethtool_ops fwnet_ethtool_ops = {
-       .get_drvinfo = fwnet_get_drvinfo,
-};
-
 static const struct net_device_ops fwnet_netdev_ops = {
        .ndo_open       = fwnet_open,
        .ndo_stop       = fwnet_stop,
        net->hard_header_len    = FWNET_HLEN;
        net->type               = ARPHRD_IEEE1394;
        net->tx_queue_len       = 10;
-       SET_ETHTOOL_OPS(net, &fwnet_ethtool_ops);
 }
 
 /* caller must hold fwnet_device_mutex */
 
 #include <linux/tcp.h>
 #include <linux/skbuff.h>
 #include <linux/bitops.h>
-#include <linux/ethtool.h>
 #include <asm/uaccess.h>
 #include <asm/delay.h>
 #include <asm/unaligned.h>
                                struct net_device *dev);
 static void ether1394_iso(struct hpsb_iso *iso);
 
-static const struct ethtool_ops ethtool_ops;
-
 static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
                           quadlet_t *data, u64 addr, size_t len, u16 flags);
 static void ether1394_add_host(struct hpsb_host *host);
        dev->header_ops         = ðer1394_header_ops;
        dev->netdev_ops         = ðer1394_netdev_ops;
 
-       SET_ETHTOOL_OPS(dev, ðtool_ops);
-
        dev->watchdog_timeo     = ETHER1394_TIMEOUT;
        dev->flags              = IFF_BROADCAST | IFF_MULTICAST;
        dev->features           = NETIF_F_HIGHDMA;
        return NETDEV_TX_OK;
 }
 
-static void ether1394_get_drvinfo(struct net_device *dev,
-                                 struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, driver_name);
-       strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */
-}
-
-static const struct ethtool_ops ethtool_ops = {
-       .get_drvinfo = ether1394_get_drvinfo
-};
-
 static int __init ether1394_init_module(void)
 {
        int err;
 
        }
        spin_unlock_irqrestore(&bp->lock, flags);
 }
-static void bmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-       struct bmac_data *bp = netdev_priv(dev);
-       strcpy(info->driver, "bmac");
-       strcpy(info->bus_info, dev_name(&bp->mdev->ofdev.dev));
-}
 
 static const struct ethtool_ops bmac_ethtool_ops = {
-       .get_drvinfo            = bmac_get_drvinfo,
        .get_link               = ethtool_op_get_link,
 };
 
 
 
 
 /* ethtool interface */
-static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
-               struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, DRIVER_NAME);
-}
 
 static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
 }
 
 static const struct ethtool_ops mpc52xx_fec_ethtool_ops = {
-       .get_drvinfo = mpc52xx_fec_get_drvinfo,
        .get_settings = mpc52xx_fec_get_settings,
        .set_settings = mpc52xx_fec_set_settings,
        .get_link = ethtool_op_get_link,
 
        return phy_ethtool_sset(phydev, cmd);
 }
 
-static void
-pasemi_mac_ethtool_get_drvinfo(struct net_device *netdev,
-                              struct ethtool_drvinfo *drvinfo)
-{
-       struct pasemi_mac *mac;
-       mac = netdev_priv(netdev);
-
-       /* clear and fill out info */
-       memset(drvinfo, 0, sizeof(struct ethtool_drvinfo));
-       strncpy(drvinfo->driver, "pasemi_mac", 12);
-       strcpy(drvinfo->version, "N/A");
-       strcpy(drvinfo->fw_version, "N/A");
-       strncpy(drvinfo->bus_info, pci_name(mac->pdev), 32);
-}
-
 static u32
 pasemi_mac_ethtool_get_msglevel(struct net_device *netdev)
 {
 const struct ethtool_ops pasemi_mac_ethtool_ops = {
        .get_settings           = pasemi_mac_ethtool_get_settings,
        .set_settings           = pasemi_mac_ethtool_set_settings,
-       .get_drvinfo            = pasemi_mac_ethtool_get_drvinfo,
        .get_msglevel           = pasemi_mac_ethtool_get_msglevel,
        .set_msglevel           = pasemi_mac_ethtool_set_msglevel,
        .get_link               = ethtool_op_get_link,
 
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/ioport.h>
-#include <linux/ethtool.h>
 #include <linux/bitops.h>
 #include <linux/mii.h>
 
 static int el3_close(struct net_device *dev);
 static void el3_tx_timeout(struct net_device *dev);
 static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static const struct ethtool_ops netdev_ethtool_ops;
 static void set_rx_mode(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 
        link->conf.ConfigIndex = 1;
 
        dev->netdev_ops = &el3_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
        dev->watchdog_timeo = TX_TIMEOUT;
 
        return tc574_config(link);
        return worklimit;
 }
 
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, "3c574_cs");
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-};
-
 /* Provide ioctl() calls to examine the MII xcvr state. */
 static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
-#include <linux/ethtool.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/crc32.h>
 static struct net_device_stats *get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void axnet_tx_timeout(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
 static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
 static void ei_watchdog(u_long arg);
 static void axnet_reset_8390(struct net_device *dev);
 
     dev->netdev_ops = &axnet_netdev_ops;
 
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
     dev->watchdog_timeo = TX_TIMEOUT;
 
     return axnet_config(link);
     add_timer(&info->watchdog);
 }
 
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, "axnet_cs");
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-};
-
 /*====================================================================*/
 
 static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/module.h>
-#include <linux/ethtool.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
 #include <linux/ibmtr.h>
     struct tok_info    *ti;
 } ibmtr_dev_t;
 
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, "ibmtr_cs");
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-};
-
 static irqreturn_t ibmtr_interrupt(int irq, void *dev_id) {
        ibmtr_dev_t *info = dev_id;
        struct net_device *dev = info->dev;
 
     info->dev = dev;
 
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
-
     return ibmtr_config(link);
 } /* ibmtr_attach */
 
 
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
-#include <linux/ethtool.h>
 #include <linux/netdevice.h>
 #include <linux/log2.h>
 #include <linux/etherdevice.h>
 static int pcnet_open(struct net_device *dev);
 static int pcnet_close(struct net_device *dev);
 static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static const struct ethtool_ops netdev_ethtool_ops;
 static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
 static void ei_watchdog(u_long arg);
 static void pcnet_reset_8390(struct net_device *dev);
     ei_status.word16 = 1;
     ei_status.reset_8390 = &pcnet_reset_8390;
 
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
-
     if (info->flags & (IS_DL10019|IS_DL10022))
        mii_phy_probe(dev);
 
 
 /*====================================================================*/
 
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, "pcnet_cs");
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-};
-
-/*====================================================================*/
-
 
 static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 
        return 0;
 }
 
-static void sc92031_ethtool_get_drvinfo(struct net_device *dev,
-               struct ethtool_drvinfo *drvinfo)
-{
-       struct sc92031_priv *priv = netdev_priv(dev);
-       struct pci_dev *pdev = priv->pdev;
-
-       strcpy(drvinfo->driver, SC92031_NAME);
-       strcpy(drvinfo->bus_info, pci_name(pdev));
-}
-
 static void sc92031_ethtool_get_wol(struct net_device *dev,
                struct ethtool_wolinfo *wolinfo)
 {
 static const struct ethtool_ops sc92031_ethtool_ops = {
        .get_settings           = sc92031_ethtool_get_settings,
        .set_settings           = sc92031_ethtool_set_settings,
-       .get_drvinfo            = sc92031_ethtool_get_drvinfo,
        .get_wol                = sc92031_ethtool_get_wol,
        .set_wol                = sc92031_ethtool_set_wol,
        .nway_reset             = sc92031_ethtool_nway_reset,
 
 #include <linux/skbuff.h>
 #include <linux/delay.h>
 #include <linux/init.h>
-#include <linux/ethtool.h>
 #include <linux/bitops.h>
 
 #include <asm/uaccess.h>
 }
 #endif
 
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       struct xircom_private *private = netdev_priv(dev);
-
-       strcpy(info->driver, "xircom_cb");
-       strcpy(info->bus_info, pci_name(private->pdev));
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-};
-
 static const struct net_device_ops netdev_ops = {
        .ndo_open               = xircom_open,
        .ndo_stop               = xircom_close,
        setup_descriptors(private);
 
        dev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
        pci_set_drvdata(pdev, dev);
 
        if (register_netdev(dev)) {
 
        return NETDEV_TX_OK;
 }
 
-static void hso_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
-{
-       struct hso_net *odev = netdev_priv(net);
-
-       strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN);
-       usb_make_path(odev->parent->usb, info->bus_info, sizeof info->bus_info);
-}
-
 static const struct ethtool_ops ops = {
-       .get_drvinfo = hso_get_drvinfo,
        .get_link = ethtool_op_get_link
 };
 
 
        return 0;
 }
 
-static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-       struct kaweth_device *kaweth = netdev_priv(dev);
-
-       strlcpy(info->driver, driver_name, sizeof(info->driver));
-       usb_make_path(kaweth->dev, info->bus_info, sizeof (info->bus_info));
-}
-
 static u32 kaweth_get_link(struct net_device *dev)
 {
        struct kaweth_device *kaweth = netdev_priv(dev);
 }
 
 static const struct ethtool_ops ops = {
-       .get_drvinfo    = kaweth_get_drvinfo,
        .get_link       = kaweth_get_link
 };
 
 
        return 0;
 }
 
-static void virtnet_get_drvinfo(struct net_device *dev,
-                               struct ethtool_drvinfo *drvinfo)
-{
-       struct virtnet_info *vi = netdev_priv(dev);
-       struct virtio_device *vdev = vi->vdev;
-
-       strncpy(drvinfo->driver, KBUILD_MODNAME, ARRAY_SIZE(drvinfo->driver));
-       strncpy(drvinfo->version, "N/A", ARRAY_SIZE(drvinfo->version));
-       strncpy(drvinfo->fw_version, "N/A", ARRAY_SIZE(drvinfo->fw_version));
-       strncpy(drvinfo->bus_info, dev_name(&vdev->dev),
-               ARRAY_SIZE(drvinfo->bus_info));
-}
-
 static int virtnet_set_tx_csum(struct net_device *dev, u32 data)
 {
        struct virtnet_info *vi = netdev_priv(dev);
 }
 
 static const struct ethtool_ops virtnet_ethtool_ops = {
-       .get_drvinfo = virtnet_get_drvinfo,
        .set_tx_csum = virtnet_set_tx_csum,
        .set_sg = ethtool_op_set_sg,
        .set_tso = ethtool_op_set_tso,
 
 #include <linux/if_arp.h>
 #include <linux/ioport.h>
 #include <linux/skbuff.h>
-#include <linux/ethtool.h>
 #include <linux/ieee80211.h>
 
 #include <pcmcia/cs.h>
 static struct net_device_stats *ray_get_stats(struct net_device *dev);
 static int ray_dev_init(struct net_device *dev);
 
-static const struct ethtool_ops netdev_ethtool_ops;
-
 static int ray_open(struct net_device *dev);
 static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb,
                                            struct net_device *dev);
 
        /* Raylink entries in the device structure */
        dev->netdev_ops = &ray_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
        dev->wireless_handlers = &ray_handler_def;
 #ifdef WIRELESS_SPY
        local->wireless_data.spy_data = &local->spy_data;
        }
 } /* end encapsulate_frame */
 
-/*===========================================================================*/
-
-static void netdev_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *info)
-{
-       strcpy(info->driver, "ray_cs");
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo = netdev_get_drvinfo,
-};
-
 /*====================================================================*/
 
 /*------------------------------------------------------------------*/
 
 
 #include <linux/delay.h>
 #include <linux/types.h>
-#include <linux/ethtool.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
        return wstats;
 }
 
-static void wl3501_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-       strlcpy(info->driver, "wl3501_cs", sizeof(info->driver));
-}
-
-static const struct ethtool_ops ops = {
-       .get_drvinfo = wl3501_get_drvinfo
-};
-
 /**
  * wl3501_detach - deletes a driver "instance"
  * @link - FILL_IN
        this->p_dev = p_dev;
        dev->wireless_data      = &this->wireless_data;
        dev->wireless_handlers  = &wl3501_handler_def;
-       SET_ETHTOOL_OPS(dev, &ops);
        netif_stop_queue(dev);
        p_dev->priv = dev;
 
 
        struct ethtool_drvinfo info;
        const struct ethtool_ops *ops = dev->ethtool_ops;
 
-       if (!ops->get_drvinfo)
-               return -EOPNOTSUPP;
-
        memset(&info, 0, sizeof(info));
        info.cmd = ETHTOOL_GDRVINFO;
-       ops->get_drvinfo(dev, &info);
+       if (ops && ops->get_drvinfo) {
+               ops->get_drvinfo(dev, &info);
+       } else if (dev->dev.parent && dev->dev.parent->driver) {
+               strlcpy(info.bus_info, dev_name(dev->dev.parent),
+                       sizeof(info.bus_info));
+               strlcpy(info.driver, dev->dev.parent->driver->name,
+                       sizeof(info.driver));
+       } else {
+               return -EOPNOTSUPP;
+       }
 
        /*
         * this method of obtaining string set info is deprecated;
         * Use ETHTOOL_GSSET_INFO instead.
         */
-       if (ops->get_sset_count) {
+       if (ops && ops->get_sset_count) {
                int rc;
 
                rc = ops->get_sset_count(dev, ETH_SS_TEST);
                if (rc >= 0)
                        info.n_priv_flags = rc;
        }
-       if (ops->get_regs_len)
+       if (ops && ops->get_regs_len)
                info.regdump_len = ops->get_regs_len(dev);
-       if (ops->get_eeprom_len)
+       if (ops && ops->get_eeprom_len)
                info.eedump_len = ops->get_eeprom_len(dev);
 
        if (copy_to_user(useraddr, &info, sizeof(info)))
        if (!dev || !netif_device_present(dev))
                return -ENODEV;
 
-       if (!dev->ethtool_ops)
-               return -EOPNOTSUPP;
-
        if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
                return -EFAULT;
 
+       if (!dev->ethtool_ops) {
+               /* ETHTOOL_GDRVINFO does not require any driver support.
+                * It is also unprivileged and does not change anything,
+                * so we can take a shortcut to it. */
+               if (ethcmd == ETHTOOL_GDRVINFO)
+                       return ethtool_get_drvinfo(dev, useraddr);
+               else
+                       return -EOPNOTSUPP;
+       }
+
        /* Allow some commands to be done by anyone */
        switch (ethcmd) {
        case ETHTOOL_GDRVINFO: