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.hyde@xxxxxxxxxxxx> Cc: Mario Limonciello <mario.limonciello@xxxxxxxx> Cc: Oliver Neukum <oliver@xxxxxxxxxx> Cc: netdev@xxxxxxxxxxxxxxx Cc: linux-usb@xxxxxxxxxxxxxxx --- 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