root/drivers/net/usb/huawei_cdc_ncm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. huawei_cdc_ncm_manage_power
  2. huawei_cdc_ncm_wdm_manage_power
  3. huawei_cdc_ncm_bind
  4. huawei_cdc_ncm_unbind
  5. huawei_cdc_ncm_suspend
  6. huawei_cdc_ncm_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as
   3  * transport layer.
   4  * Copyright (C) 2013    Enrico Mioso <mrkiko.rs@gmail.com>
   5  *
   6  * ABSTRACT:
   7  * This driver handles devices resembling the CDC NCM standard, but
   8  * encapsulating another protocol inside it. An example are some Huawei 3G
   9  * devices, exposing an embedded AT channel where you can set up the NCM
  10  * connection.
  11  * This code has been heavily inspired by the cdc_mbim.c driver, which is
  12  * Copyright (c) 2012  Smith Micro Software, Inc.
  13  * Copyright (c) 2012  Bjørn Mork <bjorn@mork.no>
  14  */
  15 
  16 #include <linux/module.h>
  17 #include <linux/netdevice.h>
  18 #include <linux/ethtool.h>
  19 #include <linux/if_vlan.h>
  20 #include <linux/ip.h>
  21 #include <linux/mii.h>
  22 #include <linux/usb.h>
  23 #include <linux/usb/cdc.h>
  24 #include <linux/usb/usbnet.h>
  25 #include <linux/usb/cdc-wdm.h>
  26 #include <linux/usb/cdc_ncm.h>
  27 
  28 /* Driver data */
  29 struct huawei_cdc_ncm_state {
  30         struct cdc_ncm_ctx *ctx;
  31         atomic_t pmcount;
  32         struct usb_driver *subdriver;
  33         struct usb_interface *control;
  34         struct usb_interface *data;
  35 };
  36 
  37 static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on)
  38 {
  39         struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
  40         int rv;
  41 
  42         if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) ||
  43                         (!on && atomic_dec_and_test(&drvstate->pmcount))) {
  44                 rv = usb_autopm_get_interface(usbnet_dev->intf);
  45                 usbnet_dev->intf->needs_remote_wakeup = on;
  46                 if (!rv)
  47                         usb_autopm_put_interface(usbnet_dev->intf);
  48         }
  49         return 0;
  50 }
  51 
  52 static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf,
  53                                            int status)
  54 {
  55         struct usbnet *usbnet_dev = usb_get_intfdata(intf);
  56 
  57         /* can be called while disconnecting */
  58         if (!usbnet_dev)
  59                 return 0;
  60 
  61         return huawei_cdc_ncm_manage_power(usbnet_dev, status);
  62 }
  63 
  64 
  65 static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev,
  66                                struct usb_interface *intf)
  67 {
  68         struct cdc_ncm_ctx *ctx;
  69         struct usb_driver *subdriver = ERR_PTR(-ENODEV);
  70         int ret = -ENODEV;
  71         struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
  72         int drvflags = 0;
  73 
  74         /* altsetting should always be 1 for NCM devices - so we hard-coded
  75          * it here. Some huawei devices will need the NDP part of the NCM package to
  76          * be at the end of the frame.
  77          */
  78         drvflags |= CDC_NCM_FLAG_NDP_TO_END;
  79 
  80         /* Additionally, it has been reported that some Huawei E3372H devices, with
  81          * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence
  82          * needing to be set to the NTB16 one again.
  83          */
  84         drvflags |= CDC_NCM_FLAG_RESET_NTB16;
  85         ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags);
  86         if (ret)
  87                 goto err;
  88 
  89         ctx = drvstate->ctx;
  90 
  91         if (usbnet_dev->status)
  92                 /* The wMaxCommand buffer must be big enough to hold
  93                  * any message from the modem. Experience has shown
  94                  * that some replies are more than 256 bytes long
  95                  */
  96                 subdriver = usb_cdc_wdm_register(ctx->control,
  97                                                  &usbnet_dev->status->desc,
  98                                                  1024, /* wMaxCommand */
  99                                                  huawei_cdc_ncm_wdm_manage_power);
 100         if (IS_ERR(subdriver)) {
 101                 ret = PTR_ERR(subdriver);
 102                 cdc_ncm_unbind(usbnet_dev, intf);
 103                 goto err;
 104         }
 105 
 106         /* Prevent usbnet from using the status descriptor */
 107         usbnet_dev->status = NULL;
 108 
 109         drvstate->subdriver = subdriver;
 110 
 111 err:
 112         return ret;
 113 }
 114 
 115 static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev,
 116                                   struct usb_interface *intf)
 117 {
 118         struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
 119         struct cdc_ncm_ctx *ctx = drvstate->ctx;
 120 
 121         if (drvstate->subdriver && drvstate->subdriver->disconnect)
 122                 drvstate->subdriver->disconnect(ctx->control);
 123         drvstate->subdriver = NULL;
 124 
 125         cdc_ncm_unbind(usbnet_dev, intf);
 126 }
 127 
 128 static int huawei_cdc_ncm_suspend(struct usb_interface *intf,
 129                                   pm_message_t message)
 130 {
 131         int ret = 0;
 132         struct usbnet *usbnet_dev = usb_get_intfdata(intf);
 133         struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
 134         struct cdc_ncm_ctx *ctx = drvstate->ctx;
 135 
 136         if (ctx == NULL) {
 137                 ret = -ENODEV;
 138                 goto error;
 139         }
 140 
 141         ret = usbnet_suspend(intf, message);
 142         if (ret < 0)
 143                 goto error;
 144 
 145         if (intf == ctx->control &&
 146                 drvstate->subdriver &&
 147                 drvstate->subdriver->suspend)
 148                 ret = drvstate->subdriver->suspend(intf, message);
 149         if (ret < 0)
 150                 usbnet_resume(intf);
 151 
 152 error:
 153         return ret;
 154 }
 155 
 156 static int huawei_cdc_ncm_resume(struct usb_interface *intf)
 157 {
 158         int ret = 0;
 159         struct usbnet *usbnet_dev = usb_get_intfdata(intf);
 160         struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
 161         bool callsub;
 162         struct cdc_ncm_ctx *ctx = drvstate->ctx;
 163 
 164         /* should we call subdriver's resume function? */
 165         callsub =
 166                 (intf == ctx->control &&
 167                 drvstate->subdriver &&
 168                 drvstate->subdriver->resume);
 169 
 170         if (callsub)
 171                 ret = drvstate->subdriver->resume(intf);
 172         if (ret < 0)
 173                 goto err;
 174         ret = usbnet_resume(intf);
 175         if (ret < 0 && callsub)
 176                 drvstate->subdriver->suspend(intf, PMSG_SUSPEND);
 177 err:
 178         return ret;
 179 }
 180 
 181 static const struct driver_info huawei_cdc_ncm_info = {
 182         .description = "Huawei CDC NCM device",
 183         .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
 184         .bind = huawei_cdc_ncm_bind,
 185         .unbind = huawei_cdc_ncm_unbind,
 186         .manage_power = huawei_cdc_ncm_manage_power,
 187         .rx_fixup = cdc_ncm_rx_fixup,
 188         .tx_fixup = cdc_ncm_tx_fixup,
 189 };
 190 
 191 static const struct usb_device_id huawei_cdc_ncm_devs[] = {
 192         /* Huawei NCM devices disguised as vendor specific */
 193         { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16),
 194           .driver_info = (unsigned long)&huawei_cdc_ncm_info,
 195         },
 196         { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46),
 197           .driver_info = (unsigned long)&huawei_cdc_ncm_info,
 198         },
 199         { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76),
 200           .driver_info = (unsigned long)&huawei_cdc_ncm_info,
 201         },
 202         { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x03, 0x16),
 203           .driver_info = (unsigned long)&huawei_cdc_ncm_info,
 204         },
 205 
 206         /* Terminating entry */
 207         {
 208         },
 209 };
 210 MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs);
 211 
 212 static struct usb_driver huawei_cdc_ncm_driver = {
 213         .name = "huawei_cdc_ncm",
 214         .id_table = huawei_cdc_ncm_devs,
 215         .probe = usbnet_probe,
 216         .disconnect = usbnet_disconnect,
 217         .suspend = huawei_cdc_ncm_suspend,
 218         .resume = huawei_cdc_ncm_resume,
 219         .reset_resume = huawei_cdc_ncm_resume,
 220         .supports_autosuspend = 1,
 221         .disable_hub_initiated_lpm = 1,
 222 };
 223 module_usb_driver(huawei_cdc_ncm_driver);
 224 MODULE_AUTHOR("Enrico Mioso <mrkiko.rs@gmail.com>");
 225 MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support");
 226 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */