12a54f2c7SM Chetan Kumar // SPDX-License-Identifier: GPL-2.0-only 22a54f2c7SM Chetan Kumar /* 32a54f2c7SM Chetan Kumar * Copyright (C) 2020-21 Intel Corporation. 42a54f2c7SM Chetan Kumar */ 52a54f2c7SM Chetan Kumar 62a54f2c7SM Chetan Kumar #include <linux/etherdevice.h> 72a54f2c7SM Chetan Kumar #include <linux/if_arp.h> 82a54f2c7SM Chetan Kumar #include <linux/if_link.h> 92a54f2c7SM Chetan Kumar #include <linux/rtnetlink.h> 102a54f2c7SM Chetan Kumar #include <linux/wwan.h> 112a54f2c7SM Chetan Kumar 122a54f2c7SM Chetan Kumar #include "iosm_ipc_chnl_cfg.h" 132a54f2c7SM Chetan Kumar #include "iosm_ipc_imem_ops.h" 142a54f2c7SM Chetan Kumar #include "iosm_ipc_wwan.h" 152a54f2c7SM Chetan Kumar 162a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_MASK 0xF0 172a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_IPV4 0x40 182a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_IPV6 0x60 192a54f2c7SM Chetan Kumar 202a54f2c7SM Chetan Kumar #define IOSM_IF_ID_PAYLOAD 2 212a54f2c7SM Chetan Kumar 222a54f2c7SM Chetan Kumar /** 2369940924SSergey Ryazanov * struct iosm_netdev_priv - netdev WWAN driver specific private data 242a54f2c7SM Chetan Kumar * @ipc_wwan: Pointer to iosm_wwan struct 252a54f2c7SM Chetan Kumar * @netdev: Pointer to network interface device structure 262a54f2c7SM Chetan Kumar * @if_id: Interface id for device. 272a54f2c7SM Chetan Kumar * @ch_id: IPC channel number for which interface device is created. 282a54f2c7SM Chetan Kumar */ 292a54f2c7SM Chetan Kumar struct iosm_netdev_priv { 302a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan; 312a54f2c7SM Chetan Kumar struct net_device *netdev; 322a54f2c7SM Chetan Kumar int if_id; 332a54f2c7SM Chetan Kumar int ch_id; 342a54f2c7SM Chetan Kumar }; 352a54f2c7SM Chetan Kumar 362a54f2c7SM Chetan Kumar /** 372a54f2c7SM Chetan Kumar * struct iosm_wwan - This structure contains information about WWAN root device 382a54f2c7SM Chetan Kumar * and interface to the IPC layer. 392a54f2c7SM Chetan Kumar * @ipc_imem: Pointer to imem data-struct 402a54f2c7SM Chetan Kumar * @sub_netlist: List of active netdevs 412a54f2c7SM Chetan Kumar * @dev: Pointer device structure 422a54f2c7SM Chetan Kumar * @if_mutex: Mutex used for add and remove interface id 432a54f2c7SM Chetan Kumar */ 442a54f2c7SM Chetan Kumar struct iosm_wwan { 452a54f2c7SM Chetan Kumar struct iosm_imem *ipc_imem; 462a54f2c7SM Chetan Kumar struct iosm_netdev_priv __rcu *sub_netlist[IP_MUX_SESSION_END + 1]; 472a54f2c7SM Chetan Kumar struct device *dev; 482a54f2c7SM Chetan Kumar struct mutex if_mutex; /* Mutex used for add and remove interface id */ 492a54f2c7SM Chetan Kumar }; 502a54f2c7SM Chetan Kumar 512a54f2c7SM Chetan Kumar /* Bring-up the wwan net link */ 522a54f2c7SM Chetan Kumar static int ipc_wwan_link_open(struct net_device *netdev) 532a54f2c7SM Chetan Kumar { 5469940924SSergey Ryazanov struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev); 552a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = priv->ipc_wwan; 562a54f2c7SM Chetan Kumar int if_id = priv->if_id; 572a54f2c7SM Chetan Kumar int ret; 582a54f2c7SM Chetan Kumar 592a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 602a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 612a54f2c7SM Chetan Kumar return -EINVAL; 622a54f2c7SM Chetan Kumar 632a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 642a54f2c7SM Chetan Kumar 652a54f2c7SM Chetan Kumar /* get channel id */ 662a54f2c7SM Chetan Kumar priv->ch_id = ipc_imem_sys_wwan_open(ipc_wwan->ipc_imem, if_id); 672a54f2c7SM Chetan Kumar 682a54f2c7SM Chetan Kumar if (priv->ch_id < 0) { 692a54f2c7SM Chetan Kumar dev_err(ipc_wwan->dev, 702a54f2c7SM Chetan Kumar "cannot connect wwan0 & id %d to the IPC mem layer", 712a54f2c7SM Chetan Kumar if_id); 722a54f2c7SM Chetan Kumar ret = -ENODEV; 732a54f2c7SM Chetan Kumar goto out; 742a54f2c7SM Chetan Kumar } 752a54f2c7SM Chetan Kumar 762a54f2c7SM Chetan Kumar /* enable tx path, DL data may follow */ 772a54f2c7SM Chetan Kumar netif_start_queue(netdev); 782a54f2c7SM Chetan Kumar 792a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "Channel id %d allocated to if_id %d", 802a54f2c7SM Chetan Kumar priv->ch_id, priv->if_id); 812a54f2c7SM Chetan Kumar 822a54f2c7SM Chetan Kumar ret = 0; 832a54f2c7SM Chetan Kumar out: 842a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 852a54f2c7SM Chetan Kumar return ret; 862a54f2c7SM Chetan Kumar } 872a54f2c7SM Chetan Kumar 882a54f2c7SM Chetan Kumar /* Bring-down the wwan net link */ 892a54f2c7SM Chetan Kumar static int ipc_wwan_link_stop(struct net_device *netdev) 902a54f2c7SM Chetan Kumar { 9169940924SSergey Ryazanov struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev); 922a54f2c7SM Chetan Kumar 932a54f2c7SM Chetan Kumar netif_stop_queue(netdev); 942a54f2c7SM Chetan Kumar 952a54f2c7SM Chetan Kumar mutex_lock(&priv->ipc_wwan->if_mutex); 962a54f2c7SM Chetan Kumar ipc_imem_sys_wwan_close(priv->ipc_wwan->ipc_imem, priv->if_id, 972a54f2c7SM Chetan Kumar priv->ch_id); 982a54f2c7SM Chetan Kumar priv->ch_id = -1; 992a54f2c7SM Chetan Kumar mutex_unlock(&priv->ipc_wwan->if_mutex); 1002a54f2c7SM Chetan Kumar 1012a54f2c7SM Chetan Kumar return 0; 1022a54f2c7SM Chetan Kumar } 1032a54f2c7SM Chetan Kumar 1042a54f2c7SM Chetan Kumar /* Transmit a packet */ 1052a54f2c7SM Chetan Kumar static int ipc_wwan_link_transmit(struct sk_buff *skb, 1062a54f2c7SM Chetan Kumar struct net_device *netdev) 1072a54f2c7SM Chetan Kumar { 10869940924SSergey Ryazanov struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev); 1092a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = priv->ipc_wwan; 1102a54f2c7SM Chetan Kumar int if_id = priv->if_id; 1112a54f2c7SM Chetan Kumar int ret; 1122a54f2c7SM Chetan Kumar 1132a54f2c7SM Chetan Kumar /* Interface IDs from 1 to 8 are for IP data 1142a54f2c7SM Chetan Kumar * & from 257 to 261 are for non-IP data 1152a54f2c7SM Chetan Kumar */ 1162a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 1172a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 1182a54f2c7SM Chetan Kumar return -EINVAL; 1192a54f2c7SM Chetan Kumar 1202a54f2c7SM Chetan Kumar /* Send the SKB to device for transmission */ 1212a54f2c7SM Chetan Kumar ret = ipc_imem_sys_wwan_transmit(ipc_wwan->ipc_imem, 1222a54f2c7SM Chetan Kumar if_id, priv->ch_id, skb); 1232a54f2c7SM Chetan Kumar 1242a54f2c7SM Chetan Kumar /* Return code of zero is success */ 1252a54f2c7SM Chetan Kumar if (ret == 0) { 1262a54f2c7SM Chetan Kumar ret = NETDEV_TX_OK; 1272a54f2c7SM Chetan Kumar } else if (ret == -EBUSY) { 1282a54f2c7SM Chetan Kumar ret = NETDEV_TX_BUSY; 1292a54f2c7SM Chetan Kumar dev_err(ipc_wwan->dev, "unable to push packets"); 1302a54f2c7SM Chetan Kumar } else { 1312a54f2c7SM Chetan Kumar goto exit; 1322a54f2c7SM Chetan Kumar } 1332a54f2c7SM Chetan Kumar 1342a54f2c7SM Chetan Kumar return ret; 1352a54f2c7SM Chetan Kumar 1362a54f2c7SM Chetan Kumar exit: 1372a54f2c7SM Chetan Kumar /* Log any skb drop */ 1382a54f2c7SM Chetan Kumar if (if_id) 1392a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "skb dropped. IF_ID: %d, ret: %d", if_id, 1402a54f2c7SM Chetan Kumar ret); 1412a54f2c7SM Chetan Kumar 1422a54f2c7SM Chetan Kumar dev_kfree_skb_any(skb); 1432a54f2c7SM Chetan Kumar return ret; 1442a54f2c7SM Chetan Kumar } 1452a54f2c7SM Chetan Kumar 1462a54f2c7SM Chetan Kumar /* Ops structure for wwan net link */ 1472a54f2c7SM Chetan Kumar static const struct net_device_ops ipc_inm_ops = { 1482a54f2c7SM Chetan Kumar .ndo_open = ipc_wwan_link_open, 1492a54f2c7SM Chetan Kumar .ndo_stop = ipc_wwan_link_stop, 1502a54f2c7SM Chetan Kumar .ndo_start_xmit = ipc_wwan_link_transmit, 1512a54f2c7SM Chetan Kumar }; 1522a54f2c7SM Chetan Kumar 1532a54f2c7SM Chetan Kumar /* Setup function for creating new net link */ 1542a54f2c7SM Chetan Kumar static void ipc_wwan_setup(struct net_device *iosm_dev) 1552a54f2c7SM Chetan Kumar { 1562a54f2c7SM Chetan Kumar iosm_dev->header_ops = NULL; 1572a54f2c7SM Chetan Kumar iosm_dev->hard_header_len = 0; 1582a54f2c7SM Chetan Kumar iosm_dev->priv_flags |= IFF_NO_QUEUE; 1592a54f2c7SM Chetan Kumar 1602a54f2c7SM Chetan Kumar iosm_dev->type = ARPHRD_NONE; 1612a54f2c7SM Chetan Kumar iosm_dev->min_mtu = ETH_MIN_MTU; 1622a54f2c7SM Chetan Kumar iosm_dev->max_mtu = ETH_MAX_MTU; 1632a54f2c7SM Chetan Kumar 1642a54f2c7SM Chetan Kumar iosm_dev->flags = IFF_POINTOPOINT | IFF_NOARP; 1652a54f2c7SM Chetan Kumar 1662a54f2c7SM Chetan Kumar iosm_dev->netdev_ops = &ipc_inm_ops; 1672a54f2c7SM Chetan Kumar } 1682a54f2c7SM Chetan Kumar 1692a54f2c7SM Chetan Kumar /* Create new wwan net link */ 1702a54f2c7SM Chetan Kumar static int ipc_wwan_newlink(void *ctxt, struct net_device *dev, 1712a54f2c7SM Chetan Kumar u32 if_id, struct netlink_ext_ack *extack) 1722a54f2c7SM Chetan Kumar { 1732a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = ctxt; 1742a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 1752a54f2c7SM Chetan Kumar int err; 1762a54f2c7SM Chetan Kumar 1772a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 1782a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 1792a54f2c7SM Chetan Kumar return -EINVAL; 1802a54f2c7SM Chetan Kumar 18169940924SSergey Ryazanov priv = wwan_netdev_drvpriv(dev); 1822a54f2c7SM Chetan Kumar priv->if_id = if_id; 1832a54f2c7SM Chetan Kumar priv->netdev = dev; 1842a54f2c7SM Chetan Kumar priv->ipc_wwan = ipc_wwan; 1852a54f2c7SM Chetan Kumar 1862a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 1872a54f2c7SM Chetan Kumar if (rcu_access_pointer(ipc_wwan->sub_netlist[if_id])) { 1882a54f2c7SM Chetan Kumar err = -EBUSY; 1892a54f2c7SM Chetan Kumar goto out_unlock; 1902a54f2c7SM Chetan Kumar } 1912a54f2c7SM Chetan Kumar 1922a54f2c7SM Chetan Kumar err = register_netdevice(dev); 1932a54f2c7SM Chetan Kumar if (err) 1942a54f2c7SM Chetan Kumar goto out_unlock; 1952a54f2c7SM Chetan Kumar 1962a54f2c7SM Chetan Kumar rcu_assign_pointer(ipc_wwan->sub_netlist[if_id], priv); 1972a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 1982a54f2c7SM Chetan Kumar 1992a54f2c7SM Chetan Kumar netif_device_attach(dev); 2002a54f2c7SM Chetan Kumar 2012a54f2c7SM Chetan Kumar return 0; 2022a54f2c7SM Chetan Kumar 2032a54f2c7SM Chetan Kumar out_unlock: 2042a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 2052a54f2c7SM Chetan Kumar return err; 2062a54f2c7SM Chetan Kumar } 2072a54f2c7SM Chetan Kumar 2082a54f2c7SM Chetan Kumar static void ipc_wwan_dellink(void *ctxt, struct net_device *dev, 2092a54f2c7SM Chetan Kumar struct list_head *head) 2102a54f2c7SM Chetan Kumar { 21169940924SSergey Ryazanov struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(dev); 2122a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = ctxt; 2132a54f2c7SM Chetan Kumar int if_id = priv->if_id; 2142a54f2c7SM Chetan Kumar 2152a54f2c7SM Chetan Kumar if (WARN_ON(if_id < IP_MUX_SESSION_START || 2162a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist))) 2172a54f2c7SM Chetan Kumar return; 2182a54f2c7SM Chetan Kumar 2192a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 2202a54f2c7SM Chetan Kumar 2212a54f2c7SM Chetan Kumar if (WARN_ON(rcu_access_pointer(ipc_wwan->sub_netlist[if_id]) != priv)) 2222a54f2c7SM Chetan Kumar goto unlock; 2232a54f2c7SM Chetan Kumar 2242a54f2c7SM Chetan Kumar RCU_INIT_POINTER(ipc_wwan->sub_netlist[if_id], NULL); 2252a54f2c7SM Chetan Kumar /* unregistering includes synchronize_net() */ 2262a54f2c7SM Chetan Kumar unregister_netdevice(dev); 2272a54f2c7SM Chetan Kumar 2282a54f2c7SM Chetan Kumar unlock: 2292a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 2302a54f2c7SM Chetan Kumar } 2312a54f2c7SM Chetan Kumar 2322a54f2c7SM Chetan Kumar static const struct wwan_ops iosm_wwan_ops = { 2332a54f2c7SM Chetan Kumar .priv_size = sizeof(struct iosm_netdev_priv), 2342a54f2c7SM Chetan Kumar .setup = ipc_wwan_setup, 2352a54f2c7SM Chetan Kumar .newlink = ipc_wwan_newlink, 2362a54f2c7SM Chetan Kumar .dellink = ipc_wwan_dellink, 2372a54f2c7SM Chetan Kumar }; 2382a54f2c7SM Chetan Kumar 2392a54f2c7SM Chetan Kumar int ipc_wwan_receive(struct iosm_wwan *ipc_wwan, struct sk_buff *skb_arg, 2402a54f2c7SM Chetan Kumar bool dss, int if_id) 2412a54f2c7SM Chetan Kumar { 2422a54f2c7SM Chetan Kumar struct sk_buff *skb = skb_arg; 2432a54f2c7SM Chetan Kumar struct net_device_stats *stats; 2442a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 2452a54f2c7SM Chetan Kumar int ret; 2462a54f2c7SM Chetan Kumar 2472a54f2c7SM Chetan Kumar if ((skb->data[0] & IOSM_IP_TYPE_MASK) == IOSM_IP_TYPE_IPV4) 2482a54f2c7SM Chetan Kumar skb->protocol = htons(ETH_P_IP); 2492a54f2c7SM Chetan Kumar else if ((skb->data[0] & IOSM_IP_TYPE_MASK) == 2502a54f2c7SM Chetan Kumar IOSM_IP_TYPE_IPV6) 2512a54f2c7SM Chetan Kumar skb->protocol = htons(ETH_P_IPV6); 2522a54f2c7SM Chetan Kumar 2532a54f2c7SM Chetan Kumar skb->pkt_type = PACKET_HOST; 2542a54f2c7SM Chetan Kumar 255*5bb4eea0SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 256*5bb4eea0SM Chetan Kumar if_id > IP_MUX_SESSION_END) { 2572a54f2c7SM Chetan Kumar ret = -EINVAL; 2582a54f2c7SM Chetan Kumar goto free; 2592a54f2c7SM Chetan Kumar } 2602a54f2c7SM Chetan Kumar 2612a54f2c7SM Chetan Kumar rcu_read_lock(); 2622a54f2c7SM Chetan Kumar priv = rcu_dereference(ipc_wwan->sub_netlist[if_id]); 2632a54f2c7SM Chetan Kumar if (!priv) { 2642a54f2c7SM Chetan Kumar ret = -EINVAL; 2652a54f2c7SM Chetan Kumar goto unlock; 2662a54f2c7SM Chetan Kumar } 2672a54f2c7SM Chetan Kumar skb->dev = priv->netdev; 2682a54f2c7SM Chetan Kumar stats = &priv->netdev->stats; 2692a54f2c7SM Chetan Kumar stats->rx_packets++; 2702a54f2c7SM Chetan Kumar stats->rx_bytes += skb->len; 2712a54f2c7SM Chetan Kumar 2722a54f2c7SM Chetan Kumar ret = netif_rx(skb); 2732a54f2c7SM Chetan Kumar skb = NULL; 2742a54f2c7SM Chetan Kumar unlock: 2752a54f2c7SM Chetan Kumar rcu_read_unlock(); 2762a54f2c7SM Chetan Kumar free: 2772a54f2c7SM Chetan Kumar dev_kfree_skb(skb); 2782a54f2c7SM Chetan Kumar return ret; 2792a54f2c7SM Chetan Kumar } 2802a54f2c7SM Chetan Kumar 2812a54f2c7SM Chetan Kumar void ipc_wwan_tx_flowctrl(struct iosm_wwan *ipc_wwan, int if_id, bool on) 2822a54f2c7SM Chetan Kumar { 2832a54f2c7SM Chetan Kumar struct net_device *netdev; 2842a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 2852a54f2c7SM Chetan Kumar bool is_tx_blk; 2862a54f2c7SM Chetan Kumar 2872a54f2c7SM Chetan Kumar rcu_read_lock(); 2882a54f2c7SM Chetan Kumar priv = rcu_dereference(ipc_wwan->sub_netlist[if_id]); 2892a54f2c7SM Chetan Kumar if (!priv) { 2902a54f2c7SM Chetan Kumar rcu_read_unlock(); 2912a54f2c7SM Chetan Kumar return; 2922a54f2c7SM Chetan Kumar } 2932a54f2c7SM Chetan Kumar 2942a54f2c7SM Chetan Kumar netdev = priv->netdev; 2952a54f2c7SM Chetan Kumar 2962a54f2c7SM Chetan Kumar is_tx_blk = netif_queue_stopped(netdev); 2972a54f2c7SM Chetan Kumar 2982a54f2c7SM Chetan Kumar if (on) 2992a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "session id[%d]: flowctrl enable", 3002a54f2c7SM Chetan Kumar if_id); 3012a54f2c7SM Chetan Kumar 3022a54f2c7SM Chetan Kumar if (on && !is_tx_blk) 3032a54f2c7SM Chetan Kumar netif_stop_queue(netdev); 3042a54f2c7SM Chetan Kumar else if (!on && is_tx_blk) 3052a54f2c7SM Chetan Kumar netif_wake_queue(netdev); 3062a54f2c7SM Chetan Kumar rcu_read_unlock(); 3072a54f2c7SM Chetan Kumar } 3082a54f2c7SM Chetan Kumar 3092a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev) 3102a54f2c7SM Chetan Kumar { 3112a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan; 3122a54f2c7SM Chetan Kumar 3132a54f2c7SM Chetan Kumar ipc_wwan = kzalloc(sizeof(*ipc_wwan), GFP_KERNEL); 3142a54f2c7SM Chetan Kumar if (!ipc_wwan) 3152a54f2c7SM Chetan Kumar return NULL; 3162a54f2c7SM Chetan Kumar 3172a54f2c7SM Chetan Kumar ipc_wwan->dev = dev; 3182a54f2c7SM Chetan Kumar ipc_wwan->ipc_imem = ipc_imem; 3192a54f2c7SM Chetan Kumar 32083068395SSergey Ryazanov /* WWAN core will create a netdev for the default IP MUX channel */ 321ca374290SSergey Ryazanov if (wwan_register_ops(ipc_wwan->dev, &iosm_wwan_ops, ipc_wwan, 32283068395SSergey Ryazanov IP_MUX_SESSION_DEFAULT)) { 3232a54f2c7SM Chetan Kumar kfree(ipc_wwan); 3242a54f2c7SM Chetan Kumar return NULL; 3252a54f2c7SM Chetan Kumar } 3262a54f2c7SM Chetan Kumar 3272a54f2c7SM Chetan Kumar mutex_init(&ipc_wwan->if_mutex); 3282a54f2c7SM Chetan Kumar 3292a54f2c7SM Chetan Kumar return ipc_wwan; 3302a54f2c7SM Chetan Kumar } 3312a54f2c7SM Chetan Kumar 3322a54f2c7SM Chetan Kumar void ipc_wwan_deinit(struct iosm_wwan *ipc_wwan) 3332a54f2c7SM Chetan Kumar { 334322a0ba9SSergey Ryazanov /* This call will remove all child netdev(s) */ 3352a54f2c7SM Chetan Kumar wwan_unregister_ops(ipc_wwan->dev); 3362a54f2c7SM Chetan Kumar 3372a54f2c7SM Chetan Kumar mutex_destroy(&ipc_wwan->if_mutex); 3382a54f2c7SM Chetan Kumar 3392a54f2c7SM Chetan Kumar kfree(ipc_wwan); 3402a54f2c7SM Chetan Kumar } 341