This patch adds support for pushing a MAC address out to USB based
ethernet controllers driven by cdc_ncm.  With this change, ifconfig can
now set the device's MAC address.  For example, the Dell Universal Dock
D6000 is driven by cdc_ncm.  The D6000 can now have its MAC address set
by ifconfig, as it can be done in Windows.  This was tested with a D6000
using ifconfig.

Signed-off-by: Charles Hyde <charles.h...@dellteam.com>
Cc: Mario Limonciello <mario.limoncie...@dell.com>
Cc: Oliver Neukum <oli...@neukum.org>
Cc: net...@vger.kernel.org
Cc: linux-usb@vger.kernel.org
---
 drivers/net/usb/cdc_ncm.c  | 20 +++++++++++++++++++-
 drivers/net/usb/usbnet.c   | 37 ++++++++++++++++++++++++++++---------
 include/linux/usb/usbnet.h |  1 +
 3 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 50c05d0f44cb..f77c8672f972 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -750,6 +750,24 @@ int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
 
+/* Provide method to push MAC address to the USB device's ethernet controller.
+ */
+int cdc_ncm_set_mac_addr(struct net_device *net, void *p)
+{
+       struct usbnet *dev = netdev_priv(net);
+       struct sockaddr *addr = p;
+
+       memcpy(dev->net->dev_addr, addr->sa_data, ETH_ALEN);
+       /*
+        * Try to push the MAC address out to the device.  Ignore any errors,
+        * to be compatible with prior versions of this source.
+        */
+       usbnet_set_ethernet_addr(dev);
+
+       return eth_mac_addr(net, p);
+}
+EXPORT_SYMBOL_GPL(cdc_ncm_set_mac_addr);
+
 static const struct net_device_ops cdc_ncm_netdev_ops = {
        .ndo_open            = usbnet_open,
        .ndo_stop            = usbnet_stop,
@@ -757,7 +775,7 @@ static const struct net_device_ops cdc_ncm_netdev_ops = {
        .ndo_tx_timeout      = usbnet_tx_timeout,
        .ndo_get_stats64     = usbnet_get_stats64,
        .ndo_change_mtu      = cdc_ncm_change_mtu,
-       .ndo_set_mac_address = eth_mac_addr,
+       .ndo_set_mac_address = cdc_ncm_set_mac_addr,
        .ndo_validate_addr   = eth_validate_addr,
 };
 
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 72514c46b478..72bdac34b0ee 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -149,20 +149,39 @@ int usbnet_get_ethernet_addr(struct usbnet *dev, int 
iMACAddress)
        int             tmp = -1, ret;
        unsigned char   buf [13];
 
-       ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
-       if (ret == 12)
-               tmp = hex2bin(dev->net->dev_addr, buf, 6);
-       if (tmp < 0) {
-               dev_dbg(&dev->udev->dev,
-                       "bad MAC string %d fetch, %d\n", iMACAddress, tmp);
-               if (ret >= 0)
-                       ret = -EINVAL;
-               return ret;
+       ret = usb_get_address(dev->udev, buf);
+       if (ret == 6)
+               memcpy(dev->net->dev_addr, buf, 6);
+       else if (ret < 0) {
+               ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
+               if (ret == 12)
+                       tmp = hex2bin(dev->net->dev_addr, buf, 6);
+               if (tmp < 0) {
+                       dev_dbg(&dev->udev->dev,
+                               "bad MAC string %d fetch, %d\n", iMACAddress,
+                               tmp);
+                       if (ret >= 0)
+                               ret = -EINVAL;
+                       return ret;
+               }
        }
        return 0;
 }
 EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr);
 
+int usbnet_set_ethernet_addr(struct usbnet *dev)
+{
+       int ret;
+
+       ret = usb_set_address(dev->udev, dev->net->dev_addr);
+       if (ret < 0) {
+               dev_dbg(&dev->udev->dev,
+                       "bad MAC address put, %d\n", ret);
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(usbnet_set_ethernet_addr);
+
 static void intr_complete (struct urb *urb)
 {
        struct usbnet   *dev = urb->context;
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index d8860f2d0976..f2b2c5ab5493 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -258,6 +258,7 @@ extern int usbnet_change_mtu(struct net_device *net, int 
new_mtu);
 
 extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *);
 extern int usbnet_get_ethernet_addr(struct usbnet *, int);
+extern int usbnet_set_ethernet_addr(struct usbnet *);
 extern void usbnet_defer_kevent(struct usbnet *, int);
 extern void usbnet_skb_return(struct usbnet *, struct sk_buff *);
 extern void usbnet_unlink_rx_urbs(struct usbnet *);
-- 
2.20.1

Reply via email to