If CONFIG_USB_NET_CDCETHER is enabled, support ECM mode through cdc_ether
driver.

Signed-off-by: Hayes Wang <hayesw...@realtek.com>
---
 drivers/net/usb/r8152.c | 255 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 247 insertions(+), 8 deletions(-)

diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 8468704..c4e8339 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -27,6 +27,7 @@
 #include <linux/usb/cdc.h>
 #include <linux/suspend.h>
 #include <linux/acpi.h>
+#include <linux/usb/usbnet.h>
 
 /* Information for net-next */
 #define NETNEXT_VERSION                "08"
@@ -4402,6 +4403,243 @@ static void rtl8152_disconnect(struct usb_interface 
*intf)
        }
 }
 
+static int rtl_usbnet_probe(struct usb_interface *intf,
+                           const struct usb_device_id *id)
+{
+       switch (id->bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_probe(intf, id);
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+       case USB_CLASS_COMM:
+               if (id->bInterfaceSubClass == USB_CDC_SUBCLASS_ETHERNET &&
+                   id->bInterfaceProtocol == USB_CDC_PROTO_NONE)
+                       return usbnet_probe(intf, id);
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+static void rtl_usbnet_disconnect(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               rtl8152_disconnect(intf);
+               break;
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+       case USB_CLASS_COMM:
+               if (alt->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_ETHERNET &&
+                   alt->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) {
+                       usbnet_disconnect(intf);
+                       break;
+               }
+#endif
+       default:
+               break;
+       }
+}
+
+static int rtl_usbnet_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_suspend(intf, message);
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+       case USB_CLASS_COMM:
+               if (alt->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_ETHERNET &&
+                   alt->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
+                       return usbnet_suspend(intf, message);
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+static int rtl_usbnet_resume(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_resume(intf);
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+       case USB_CLASS_COMM:
+               if (alt->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_ETHERNET &&
+                   alt->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
+                       return usbnet_resume(intf);
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+static int rtl_usbnet_reset_resume(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_reset_resume(intf);
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+       case USB_CLASS_COMM:
+               if (alt->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_ETHERNET &&
+                   alt->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
+                       return usbnet_resume(intf);
+#endif
+       default:
+               return -ENODEV;
+       }
+}
+
+static int rtl_usbnet_pre_reset(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       if (!usb_get_intfdata(intf))
+               return 0;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_pre_reset(intf);
+       default:
+               return 1;
+       }
+}
+
+static int rtl_usbnet_post_reset(struct usb_interface *intf)
+{
+       struct usb_host_interface *alt = intf->cur_altsetting;
+
+       if (!usb_get_intfdata(intf))
+               return 0;
+
+       switch (alt->desc.bInterfaceClass) {
+       case USB_CLASS_VENDOR_SPEC:
+               return rtl8152_post_reset(intf);
+       default:
+               return 1;
+       }
+}
+
+#if IS_ENABLED(CONFIG_USB_NET_CDCETHER)
+
+static int r815x_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       __le32 tmp;
+       u16 index;
+       u8 shift;
+       int err;
+
+       if (phy_id != R8152_PHY_ID)
+               return -EINVAL;
+
+       index = 0xB400 + reg * 2;
+       shift = index & 2;
+       index &= ~3;
+
+       err = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
+                             index, MCU_TYPE_PLA, &tmp, sizeof(tmp));
+
+       if (err > 0) {
+               err = __le32_to_cpu(tmp);
+               err >>= (shift * 8);
+               err &= 0xffff;
+       }
+
+       return err;
+}
+
+static
+void r815x_mdio_write(struct net_device *netdev, int phy_id, int reg, int val)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       __le32 tmp;
+       u16 index;
+       u16 byen = BYTE_EN_WORD;
+       u8 shift;
+
+       if (phy_id != R8152_PHY_ID)
+               return;
+
+       val &= 0xffff;
+       index = 0xB400 + reg * 2;
+       shift = index & 2;
+       index &= ~3;
+
+       if (shift) {
+               byen <<= shift;
+               val <<= (shift * 8);
+       }
+
+       tmp = __cpu_to_le32(val);
+
+       usbnet_write_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_WRITE, index,
+                        MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
+}
+
+static int rtl_ecm_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       __le32 tmp;
+       int err;
+
+       err = usbnet_cdc_bind(dev, intf);
+       if (err < 0)
+               goto err1;
+
+       tmp = __cpu_to_le32(0xa000);
+       err = usbnet_write_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_WRITE,
+                              PLA_OCP_GPHY_BASE, MCU_TYPE_PLA | BYTE_EN_WORD,
+                              &tmp, sizeof(tmp));
+       if (err < 0)
+               goto err2;
+
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = r815x_mdio_read;
+       dev->mii.mdio_write = r815x_mdio_write;
+       dev->mii.phy_id_mask = 0x3f;
+       dev->mii.reg_num_mask = 0x1f;
+       dev->mii.phy_id = R8152_PHY_ID;
+
+       switch (rtl_get_version(intf)) {
+       case RTL_VER_01:
+       case RTL_VER_02:
+               dev->mii.supports_gmii = 0;
+               break;
+       default:
+               dev->mii.supports_gmii = 1;
+               break;
+       }
+
+       return 0;
+
+err2:
+       usbnet_cdc_unbind(dev, intf);
+err1:
+       return err;
+}
+
+static const struct driver_info rtl_ecm_info = {
+       .description =  "RTL8152/RTL8153 ECM Device",
+       .flags =        FLAG_ETHER | FLAG_POINTTOPOINT,
+       .bind =         rtl_ecm_bind,
+       .unbind =       usbnet_cdc_unbind,
+       .status =       usbnet_cdc_status,
+       .manage_power = usbnet_manage_power,
+};
+
+#define RTL_ECM_INFO   ((unsigned long)&rtl_ecm_info)
+
+#else
+
+#define RTL_ECM_INFO   0
+
+#endif
+
 #define REALTEK_USB_DEVICE(vend, prod) \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
                       USB_DEVICE_ID_MATCH_INT_CLASS, \
@@ -4416,7 +4654,8 @@ static void rtl8152_disconnect(struct usb_interface *intf)
        .idProduct = (prod), \
        .bInterfaceClass = USB_CLASS_COMM, \
        .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
-       .bInterfaceProtocol = USB_CDC_PROTO_NONE
+       .bInterfaceProtocol = USB_CDC_PROTO_NONE, \
+       .driver_info = RTL_ECM_INFO,
 
 /* table of devices that work with this driver */
 static struct usb_device_id rtl8152_table[] = {
@@ -4434,13 +4673,13 @@ MODULE_DEVICE_TABLE(usb, rtl8152_table);
 static struct usb_driver rtl8152_driver = {
        .name =         MODULENAME,
        .id_table =     rtl8152_table,
-       .probe =        rtl8152_probe,
-       .disconnect =   rtl8152_disconnect,
-       .suspend =      rtl8152_suspend,
-       .resume =       rtl8152_resume,
-       .reset_resume = rtl8152_reset_resume,
-       .pre_reset =    rtl8152_pre_reset,
-       .post_reset =   rtl8152_post_reset,
+       .probe =        rtl_usbnet_probe,
+       .disconnect =   rtl_usbnet_disconnect,
+       .suspend =      rtl_usbnet_suspend,
+       .resume =       rtl_usbnet_resume,
+       .reset_resume = rtl_usbnet_reset_resume,
+       .pre_reset =    rtl_usbnet_pre_reset,
+       .post_reset =   rtl_usbnet_post_reset,
        .supports_autosuspend = 1,
        .disable_hub_initiated_lpm = 1,
 };
-- 
2.7.4

Reply via email to