Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / usb / cdc_ncm.c
index 61b74a2b89ac4fbcf2ac494fc0045645efc379a0..43afde8f48d23e2cc3ee15e4cc55bd298b45dfe4 100644 (file)
 
 #define        DRIVER_VERSION                          "14-Mar-2012"
 
+#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
+static bool prefer_mbim = true;
+#else
+static bool prefer_mbim;
+#endif
+module_param(prefer_mbim, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(prefer_mbim, "Prefer MBIM setting on dual NCM/MBIM functions");
+
 static void cdc_ncm_txpath_bh(unsigned long param);
 static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
 static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
@@ -354,8 +362,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        u8 iface_no;
 
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-       if (ctx == NULL)
-               return -ENODEV;
+       if (!ctx)
+               return -ENOMEM;
 
        hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ctx->tx_timer.function = &cdc_ncm_tx_timer_cb;
@@ -550,9 +558,12 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
 
-static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
+/* Select the MBIM altsetting iff it is preferred and available,
+ * returning the number of the corresponding data interface altsetting
+ */
+u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
 {
-       int ret;
+       struct usb_host_interface *alt;
 
        /* The MBIM spec defines a NCM compatible default altsetting,
         * which we may have matched:
@@ -568,23 +579,27 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
         *   endpoint descriptors, shall be constructed according to
         *   the rules given in section 6 (USB Device Model) of this
         *   specification."
-        *
-        * Do not bind to such interfaces, allowing cdc_mbim to handle
-        * them
         */
-#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
-       if ((intf->num_altsetting == 2) &&
-           !usb_set_interface(dev->udev,
-                              intf->cur_altsetting->desc.bInterfaceNumber,
-                              CDC_NCM_COMM_ALTSETTING_MBIM)) {
-               if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
-                       return -ENODEV;
-               else
-                       usb_set_interface(dev->udev,
-                                         intf->cur_altsetting->desc.bInterfaceNumber,
-                                         CDC_NCM_COMM_ALTSETTING_NCM);
+       if (prefer_mbim && intf->num_altsetting == 2) {
+               alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
+               if (alt && cdc_ncm_comm_intf_is_mbim(alt) &&
+                   !usb_set_interface(dev->udev,
+                                      intf->cur_altsetting->desc.bInterfaceNumber,
+                                      CDC_NCM_COMM_ALTSETTING_MBIM))
+                       return CDC_NCM_DATA_ALTSETTING_MBIM;
        }
-#endif
+       return CDC_NCM_DATA_ALTSETTING_NCM;
+}
+EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
+
+static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret;
+
+       /* MBIM backwards compatible function? */
+       cdc_ncm_select_altsetting(dev, intf);
+       if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
+               return -ENODEV;
 
        /* NCM data altsetting is always 1 */
        ret = cdc_ncm_bind_common(dev, intf, 1);
@@ -595,7 +610,7 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
         * (carrier is OFF) during attach, so the IP network stack does not
         * start IPv6 negotiation and more.
         */
-       netif_carrier_off(dev->net);
+       usbnet_link_change(dev, 0, 0);
        return ret;
 }
 
@@ -1091,12 +1106,9 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                        " %sconnected\n",
                        ctx->netdev->name, ctx->connected ? "" : "dis");
 
-               if (ctx->connected)
-                       netif_carrier_on(dev->net);
-               else {
-                       netif_carrier_off(dev->net);
+               usbnet_link_change(dev, ctx->connected, 0);
+               if (!ctx->connected)
                        ctx->tx_speed = ctx->rx_speed = 0;
-               }
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
@@ -1109,8 +1121,9 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                break;
 
        default:
-               dev_err(&dev->udev->dev, "NCM: unexpected "
-                       "notification 0x%02x!\n", event->bNotificationType);
+               dev_dbg(&dev->udev->dev,
+                       "NCM: unexpected notification 0x%02x!\n",
+                       event->bNotificationType);
                break;
        }
 }