1*2a54f2c7SM Chetan Kumar // SPDX-License-Identifier: GPL-2.0-only 2*2a54f2c7SM Chetan Kumar /* 3*2a54f2c7SM Chetan Kumar * Copyright (C) 2020-21 Intel Corporation. 4*2a54f2c7SM Chetan Kumar */ 5*2a54f2c7SM Chetan Kumar 6*2a54f2c7SM Chetan Kumar #include <linux/etherdevice.h> 7*2a54f2c7SM Chetan Kumar #include <linux/if_arp.h> 8*2a54f2c7SM Chetan Kumar #include <linux/if_link.h> 9*2a54f2c7SM Chetan Kumar #include <linux/rtnetlink.h> 10*2a54f2c7SM Chetan Kumar #include <linux/wwan.h> 11*2a54f2c7SM Chetan Kumar 12*2a54f2c7SM Chetan Kumar #include "iosm_ipc_chnl_cfg.h" 13*2a54f2c7SM Chetan Kumar #include "iosm_ipc_imem_ops.h" 14*2a54f2c7SM Chetan Kumar #include "iosm_ipc_wwan.h" 15*2a54f2c7SM Chetan Kumar 16*2a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_MASK 0xF0 17*2a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_IPV4 0x40 18*2a54f2c7SM Chetan Kumar #define IOSM_IP_TYPE_IPV6 0x60 19*2a54f2c7SM Chetan Kumar 20*2a54f2c7SM Chetan Kumar #define IOSM_IF_ID_PAYLOAD 2 21*2a54f2c7SM Chetan Kumar 22*2a54f2c7SM Chetan Kumar /** 23*2a54f2c7SM Chetan Kumar * struct iosm_netdev_priv - netdev private data 24*2a54f2c7SM Chetan Kumar * @ipc_wwan: Pointer to iosm_wwan struct 25*2a54f2c7SM Chetan Kumar * @netdev: Pointer to network interface device structure 26*2a54f2c7SM Chetan Kumar * @if_id: Interface id for device. 27*2a54f2c7SM Chetan Kumar * @ch_id: IPC channel number for which interface device is created. 28*2a54f2c7SM Chetan Kumar */ 29*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv { 30*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan; 31*2a54f2c7SM Chetan Kumar struct net_device *netdev; 32*2a54f2c7SM Chetan Kumar int if_id; 33*2a54f2c7SM Chetan Kumar int ch_id; 34*2a54f2c7SM Chetan Kumar }; 35*2a54f2c7SM Chetan Kumar 36*2a54f2c7SM Chetan Kumar /** 37*2a54f2c7SM Chetan Kumar * struct iosm_wwan - This structure contains information about WWAN root device 38*2a54f2c7SM Chetan Kumar * and interface to the IPC layer. 39*2a54f2c7SM Chetan Kumar * @ipc_imem: Pointer to imem data-struct 40*2a54f2c7SM Chetan Kumar * @sub_netlist: List of active netdevs 41*2a54f2c7SM Chetan Kumar * @dev: Pointer device structure 42*2a54f2c7SM Chetan Kumar * @if_mutex: Mutex used for add and remove interface id 43*2a54f2c7SM Chetan Kumar */ 44*2a54f2c7SM Chetan Kumar struct iosm_wwan { 45*2a54f2c7SM Chetan Kumar struct iosm_imem *ipc_imem; 46*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv __rcu *sub_netlist[IP_MUX_SESSION_END + 1]; 47*2a54f2c7SM Chetan Kumar struct device *dev; 48*2a54f2c7SM Chetan Kumar struct mutex if_mutex; /* Mutex used for add and remove interface id */ 49*2a54f2c7SM Chetan Kumar }; 50*2a54f2c7SM Chetan Kumar 51*2a54f2c7SM Chetan Kumar /* Bring-up the wwan net link */ 52*2a54f2c7SM Chetan Kumar static int ipc_wwan_link_open(struct net_device *netdev) 53*2a54f2c7SM Chetan Kumar { 54*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv = netdev_priv(netdev); 55*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = priv->ipc_wwan; 56*2a54f2c7SM Chetan Kumar int if_id = priv->if_id; 57*2a54f2c7SM Chetan Kumar int ret; 58*2a54f2c7SM Chetan Kumar 59*2a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 60*2a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 61*2a54f2c7SM Chetan Kumar return -EINVAL; 62*2a54f2c7SM Chetan Kumar 63*2a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 64*2a54f2c7SM Chetan Kumar 65*2a54f2c7SM Chetan Kumar /* get channel id */ 66*2a54f2c7SM Chetan Kumar priv->ch_id = ipc_imem_sys_wwan_open(ipc_wwan->ipc_imem, if_id); 67*2a54f2c7SM Chetan Kumar 68*2a54f2c7SM Chetan Kumar if (priv->ch_id < 0) { 69*2a54f2c7SM Chetan Kumar dev_err(ipc_wwan->dev, 70*2a54f2c7SM Chetan Kumar "cannot connect wwan0 & id %d to the IPC mem layer", 71*2a54f2c7SM Chetan Kumar if_id); 72*2a54f2c7SM Chetan Kumar ret = -ENODEV; 73*2a54f2c7SM Chetan Kumar goto out; 74*2a54f2c7SM Chetan Kumar } 75*2a54f2c7SM Chetan Kumar 76*2a54f2c7SM Chetan Kumar /* enable tx path, DL data may follow */ 77*2a54f2c7SM Chetan Kumar netif_start_queue(netdev); 78*2a54f2c7SM Chetan Kumar 79*2a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "Channel id %d allocated to if_id %d", 80*2a54f2c7SM Chetan Kumar priv->ch_id, priv->if_id); 81*2a54f2c7SM Chetan Kumar 82*2a54f2c7SM Chetan Kumar ret = 0; 83*2a54f2c7SM Chetan Kumar out: 84*2a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 85*2a54f2c7SM Chetan Kumar return ret; 86*2a54f2c7SM Chetan Kumar } 87*2a54f2c7SM Chetan Kumar 88*2a54f2c7SM Chetan Kumar /* Bring-down the wwan net link */ 89*2a54f2c7SM Chetan Kumar static int ipc_wwan_link_stop(struct net_device *netdev) 90*2a54f2c7SM Chetan Kumar { 91*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv = netdev_priv(netdev); 92*2a54f2c7SM Chetan Kumar 93*2a54f2c7SM Chetan Kumar netif_stop_queue(netdev); 94*2a54f2c7SM Chetan Kumar 95*2a54f2c7SM Chetan Kumar mutex_lock(&priv->ipc_wwan->if_mutex); 96*2a54f2c7SM Chetan Kumar ipc_imem_sys_wwan_close(priv->ipc_wwan->ipc_imem, priv->if_id, 97*2a54f2c7SM Chetan Kumar priv->ch_id); 98*2a54f2c7SM Chetan Kumar priv->ch_id = -1; 99*2a54f2c7SM Chetan Kumar mutex_unlock(&priv->ipc_wwan->if_mutex); 100*2a54f2c7SM Chetan Kumar 101*2a54f2c7SM Chetan Kumar return 0; 102*2a54f2c7SM Chetan Kumar } 103*2a54f2c7SM Chetan Kumar 104*2a54f2c7SM Chetan Kumar /* Transmit a packet */ 105*2a54f2c7SM Chetan Kumar static int ipc_wwan_link_transmit(struct sk_buff *skb, 106*2a54f2c7SM Chetan Kumar struct net_device *netdev) 107*2a54f2c7SM Chetan Kumar { 108*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv = netdev_priv(netdev); 109*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = priv->ipc_wwan; 110*2a54f2c7SM Chetan Kumar int if_id = priv->if_id; 111*2a54f2c7SM Chetan Kumar int ret; 112*2a54f2c7SM Chetan Kumar 113*2a54f2c7SM Chetan Kumar /* Interface IDs from 1 to 8 are for IP data 114*2a54f2c7SM Chetan Kumar * & from 257 to 261 are for non-IP data 115*2a54f2c7SM Chetan Kumar */ 116*2a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 117*2a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 118*2a54f2c7SM Chetan Kumar return -EINVAL; 119*2a54f2c7SM Chetan Kumar 120*2a54f2c7SM Chetan Kumar /* Send the SKB to device for transmission */ 121*2a54f2c7SM Chetan Kumar ret = ipc_imem_sys_wwan_transmit(ipc_wwan->ipc_imem, 122*2a54f2c7SM Chetan Kumar if_id, priv->ch_id, skb); 123*2a54f2c7SM Chetan Kumar 124*2a54f2c7SM Chetan Kumar /* Return code of zero is success */ 125*2a54f2c7SM Chetan Kumar if (ret == 0) { 126*2a54f2c7SM Chetan Kumar ret = NETDEV_TX_OK; 127*2a54f2c7SM Chetan Kumar } else if (ret == -EBUSY) { 128*2a54f2c7SM Chetan Kumar ret = NETDEV_TX_BUSY; 129*2a54f2c7SM Chetan Kumar dev_err(ipc_wwan->dev, "unable to push packets"); 130*2a54f2c7SM Chetan Kumar } else { 131*2a54f2c7SM Chetan Kumar goto exit; 132*2a54f2c7SM Chetan Kumar } 133*2a54f2c7SM Chetan Kumar 134*2a54f2c7SM Chetan Kumar return ret; 135*2a54f2c7SM Chetan Kumar 136*2a54f2c7SM Chetan Kumar exit: 137*2a54f2c7SM Chetan Kumar /* Log any skb drop */ 138*2a54f2c7SM Chetan Kumar if (if_id) 139*2a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "skb dropped. IF_ID: %d, ret: %d", if_id, 140*2a54f2c7SM Chetan Kumar ret); 141*2a54f2c7SM Chetan Kumar 142*2a54f2c7SM Chetan Kumar dev_kfree_skb_any(skb); 143*2a54f2c7SM Chetan Kumar return ret; 144*2a54f2c7SM Chetan Kumar } 145*2a54f2c7SM Chetan Kumar 146*2a54f2c7SM Chetan Kumar /* Ops structure for wwan net link */ 147*2a54f2c7SM Chetan Kumar static const struct net_device_ops ipc_inm_ops = { 148*2a54f2c7SM Chetan Kumar .ndo_open = ipc_wwan_link_open, 149*2a54f2c7SM Chetan Kumar .ndo_stop = ipc_wwan_link_stop, 150*2a54f2c7SM Chetan Kumar .ndo_start_xmit = ipc_wwan_link_transmit, 151*2a54f2c7SM Chetan Kumar }; 152*2a54f2c7SM Chetan Kumar 153*2a54f2c7SM Chetan Kumar /* Setup function for creating new net link */ 154*2a54f2c7SM Chetan Kumar static void ipc_wwan_setup(struct net_device *iosm_dev) 155*2a54f2c7SM Chetan Kumar { 156*2a54f2c7SM Chetan Kumar iosm_dev->header_ops = NULL; 157*2a54f2c7SM Chetan Kumar iosm_dev->hard_header_len = 0; 158*2a54f2c7SM Chetan Kumar iosm_dev->priv_flags |= IFF_NO_QUEUE; 159*2a54f2c7SM Chetan Kumar 160*2a54f2c7SM Chetan Kumar iosm_dev->type = ARPHRD_NONE; 161*2a54f2c7SM Chetan Kumar iosm_dev->min_mtu = ETH_MIN_MTU; 162*2a54f2c7SM Chetan Kumar iosm_dev->max_mtu = ETH_MAX_MTU; 163*2a54f2c7SM Chetan Kumar 164*2a54f2c7SM Chetan Kumar iosm_dev->flags = IFF_POINTOPOINT | IFF_NOARP; 165*2a54f2c7SM Chetan Kumar 166*2a54f2c7SM Chetan Kumar iosm_dev->netdev_ops = &ipc_inm_ops; 167*2a54f2c7SM Chetan Kumar } 168*2a54f2c7SM Chetan Kumar 169*2a54f2c7SM Chetan Kumar /* Create new wwan net link */ 170*2a54f2c7SM Chetan Kumar static int ipc_wwan_newlink(void *ctxt, struct net_device *dev, 171*2a54f2c7SM Chetan Kumar u32 if_id, struct netlink_ext_ack *extack) 172*2a54f2c7SM Chetan Kumar { 173*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = ctxt; 174*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 175*2a54f2c7SM Chetan Kumar int err; 176*2a54f2c7SM Chetan Kumar 177*2a54f2c7SM Chetan Kumar if (if_id < IP_MUX_SESSION_START || 178*2a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist)) 179*2a54f2c7SM Chetan Kumar return -EINVAL; 180*2a54f2c7SM Chetan Kumar 181*2a54f2c7SM Chetan Kumar priv = netdev_priv(dev); 182*2a54f2c7SM Chetan Kumar priv->if_id = if_id; 183*2a54f2c7SM Chetan Kumar priv->netdev = dev; 184*2a54f2c7SM Chetan Kumar priv->ipc_wwan = ipc_wwan; 185*2a54f2c7SM Chetan Kumar 186*2a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 187*2a54f2c7SM Chetan Kumar if (rcu_access_pointer(ipc_wwan->sub_netlist[if_id])) { 188*2a54f2c7SM Chetan Kumar err = -EBUSY; 189*2a54f2c7SM Chetan Kumar goto out_unlock; 190*2a54f2c7SM Chetan Kumar } 191*2a54f2c7SM Chetan Kumar 192*2a54f2c7SM Chetan Kumar err = register_netdevice(dev); 193*2a54f2c7SM Chetan Kumar if (err) 194*2a54f2c7SM Chetan Kumar goto out_unlock; 195*2a54f2c7SM Chetan Kumar 196*2a54f2c7SM Chetan Kumar rcu_assign_pointer(ipc_wwan->sub_netlist[if_id], priv); 197*2a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 198*2a54f2c7SM Chetan Kumar 199*2a54f2c7SM Chetan Kumar netif_device_attach(dev); 200*2a54f2c7SM Chetan Kumar 201*2a54f2c7SM Chetan Kumar return 0; 202*2a54f2c7SM Chetan Kumar 203*2a54f2c7SM Chetan Kumar out_unlock: 204*2a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 205*2a54f2c7SM Chetan Kumar return err; 206*2a54f2c7SM Chetan Kumar } 207*2a54f2c7SM Chetan Kumar 208*2a54f2c7SM Chetan Kumar static void ipc_wwan_dellink(void *ctxt, struct net_device *dev, 209*2a54f2c7SM Chetan Kumar struct list_head *head) 210*2a54f2c7SM Chetan Kumar { 211*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan = ctxt; 212*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv = netdev_priv(dev); 213*2a54f2c7SM Chetan Kumar int if_id = priv->if_id; 214*2a54f2c7SM Chetan Kumar 215*2a54f2c7SM Chetan Kumar if (WARN_ON(if_id < IP_MUX_SESSION_START || 216*2a54f2c7SM Chetan Kumar if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist))) 217*2a54f2c7SM Chetan Kumar return; 218*2a54f2c7SM Chetan Kumar 219*2a54f2c7SM Chetan Kumar mutex_lock(&ipc_wwan->if_mutex); 220*2a54f2c7SM Chetan Kumar 221*2a54f2c7SM Chetan Kumar if (WARN_ON(rcu_access_pointer(ipc_wwan->sub_netlist[if_id]) != priv)) 222*2a54f2c7SM Chetan Kumar goto unlock; 223*2a54f2c7SM Chetan Kumar 224*2a54f2c7SM Chetan Kumar RCU_INIT_POINTER(ipc_wwan->sub_netlist[if_id], NULL); 225*2a54f2c7SM Chetan Kumar /* unregistering includes synchronize_net() */ 226*2a54f2c7SM Chetan Kumar unregister_netdevice(dev); 227*2a54f2c7SM Chetan Kumar 228*2a54f2c7SM Chetan Kumar unlock: 229*2a54f2c7SM Chetan Kumar mutex_unlock(&ipc_wwan->if_mutex); 230*2a54f2c7SM Chetan Kumar } 231*2a54f2c7SM Chetan Kumar 232*2a54f2c7SM Chetan Kumar static const struct wwan_ops iosm_wwan_ops = { 233*2a54f2c7SM Chetan Kumar .priv_size = sizeof(struct iosm_netdev_priv), 234*2a54f2c7SM Chetan Kumar .setup = ipc_wwan_setup, 235*2a54f2c7SM Chetan Kumar .newlink = ipc_wwan_newlink, 236*2a54f2c7SM Chetan Kumar .dellink = ipc_wwan_dellink, 237*2a54f2c7SM Chetan Kumar }; 238*2a54f2c7SM Chetan Kumar 239*2a54f2c7SM Chetan Kumar int ipc_wwan_receive(struct iosm_wwan *ipc_wwan, struct sk_buff *skb_arg, 240*2a54f2c7SM Chetan Kumar bool dss, int if_id) 241*2a54f2c7SM Chetan Kumar { 242*2a54f2c7SM Chetan Kumar struct sk_buff *skb = skb_arg; 243*2a54f2c7SM Chetan Kumar struct net_device_stats *stats; 244*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 245*2a54f2c7SM Chetan Kumar int ret; 246*2a54f2c7SM Chetan Kumar 247*2a54f2c7SM Chetan Kumar if ((skb->data[0] & IOSM_IP_TYPE_MASK) == IOSM_IP_TYPE_IPV4) 248*2a54f2c7SM Chetan Kumar skb->protocol = htons(ETH_P_IP); 249*2a54f2c7SM Chetan Kumar else if ((skb->data[0] & IOSM_IP_TYPE_MASK) == 250*2a54f2c7SM Chetan Kumar IOSM_IP_TYPE_IPV6) 251*2a54f2c7SM Chetan Kumar skb->protocol = htons(ETH_P_IPV6); 252*2a54f2c7SM Chetan Kumar 253*2a54f2c7SM Chetan Kumar skb->pkt_type = PACKET_HOST; 254*2a54f2c7SM Chetan Kumar 255*2a54f2c7SM Chetan Kumar if (if_id < (IP_MUX_SESSION_START - 1) || 256*2a54f2c7SM Chetan Kumar if_id > (IP_MUX_SESSION_END - 1)) { 257*2a54f2c7SM Chetan Kumar ret = -EINVAL; 258*2a54f2c7SM Chetan Kumar goto free; 259*2a54f2c7SM Chetan Kumar } 260*2a54f2c7SM Chetan Kumar 261*2a54f2c7SM Chetan Kumar rcu_read_lock(); 262*2a54f2c7SM Chetan Kumar priv = rcu_dereference(ipc_wwan->sub_netlist[if_id]); 263*2a54f2c7SM Chetan Kumar if (!priv) { 264*2a54f2c7SM Chetan Kumar ret = -EINVAL; 265*2a54f2c7SM Chetan Kumar goto unlock; 266*2a54f2c7SM Chetan Kumar } 267*2a54f2c7SM Chetan Kumar skb->dev = priv->netdev; 268*2a54f2c7SM Chetan Kumar stats = &priv->netdev->stats; 269*2a54f2c7SM Chetan Kumar stats->rx_packets++; 270*2a54f2c7SM Chetan Kumar stats->rx_bytes += skb->len; 271*2a54f2c7SM Chetan Kumar 272*2a54f2c7SM Chetan Kumar ret = netif_rx(skb); 273*2a54f2c7SM Chetan Kumar skb = NULL; 274*2a54f2c7SM Chetan Kumar unlock: 275*2a54f2c7SM Chetan Kumar rcu_read_unlock(); 276*2a54f2c7SM Chetan Kumar free: 277*2a54f2c7SM Chetan Kumar dev_kfree_skb(skb); 278*2a54f2c7SM Chetan Kumar return ret; 279*2a54f2c7SM Chetan Kumar } 280*2a54f2c7SM Chetan Kumar 281*2a54f2c7SM Chetan Kumar void ipc_wwan_tx_flowctrl(struct iosm_wwan *ipc_wwan, int if_id, bool on) 282*2a54f2c7SM Chetan Kumar { 283*2a54f2c7SM Chetan Kumar struct net_device *netdev; 284*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 285*2a54f2c7SM Chetan Kumar bool is_tx_blk; 286*2a54f2c7SM Chetan Kumar 287*2a54f2c7SM Chetan Kumar rcu_read_lock(); 288*2a54f2c7SM Chetan Kumar priv = rcu_dereference(ipc_wwan->sub_netlist[if_id]); 289*2a54f2c7SM Chetan Kumar if (!priv) { 290*2a54f2c7SM Chetan Kumar rcu_read_unlock(); 291*2a54f2c7SM Chetan Kumar return; 292*2a54f2c7SM Chetan Kumar } 293*2a54f2c7SM Chetan Kumar 294*2a54f2c7SM Chetan Kumar netdev = priv->netdev; 295*2a54f2c7SM Chetan Kumar 296*2a54f2c7SM Chetan Kumar is_tx_blk = netif_queue_stopped(netdev); 297*2a54f2c7SM Chetan Kumar 298*2a54f2c7SM Chetan Kumar if (on) 299*2a54f2c7SM Chetan Kumar dev_dbg(ipc_wwan->dev, "session id[%d]: flowctrl enable", 300*2a54f2c7SM Chetan Kumar if_id); 301*2a54f2c7SM Chetan Kumar 302*2a54f2c7SM Chetan Kumar if (on && !is_tx_blk) 303*2a54f2c7SM Chetan Kumar netif_stop_queue(netdev); 304*2a54f2c7SM Chetan Kumar else if (!on && is_tx_blk) 305*2a54f2c7SM Chetan Kumar netif_wake_queue(netdev); 306*2a54f2c7SM Chetan Kumar rcu_read_unlock(); 307*2a54f2c7SM Chetan Kumar } 308*2a54f2c7SM Chetan Kumar 309*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev) 310*2a54f2c7SM Chetan Kumar { 311*2a54f2c7SM Chetan Kumar struct iosm_wwan *ipc_wwan; 312*2a54f2c7SM Chetan Kumar 313*2a54f2c7SM Chetan Kumar ipc_wwan = kzalloc(sizeof(*ipc_wwan), GFP_KERNEL); 314*2a54f2c7SM Chetan Kumar if (!ipc_wwan) 315*2a54f2c7SM Chetan Kumar return NULL; 316*2a54f2c7SM Chetan Kumar 317*2a54f2c7SM Chetan Kumar ipc_wwan->dev = dev; 318*2a54f2c7SM Chetan Kumar ipc_wwan->ipc_imem = ipc_imem; 319*2a54f2c7SM Chetan Kumar 320*2a54f2c7SM Chetan Kumar if (wwan_register_ops(ipc_wwan->dev, &iosm_wwan_ops, ipc_wwan)) { 321*2a54f2c7SM Chetan Kumar kfree(ipc_wwan); 322*2a54f2c7SM Chetan Kumar return NULL; 323*2a54f2c7SM Chetan Kumar } 324*2a54f2c7SM Chetan Kumar 325*2a54f2c7SM Chetan Kumar mutex_init(&ipc_wwan->if_mutex); 326*2a54f2c7SM Chetan Kumar 327*2a54f2c7SM Chetan Kumar return ipc_wwan; 328*2a54f2c7SM Chetan Kumar } 329*2a54f2c7SM Chetan Kumar 330*2a54f2c7SM Chetan Kumar void ipc_wwan_deinit(struct iosm_wwan *ipc_wwan) 331*2a54f2c7SM Chetan Kumar { 332*2a54f2c7SM Chetan Kumar int if_id; 333*2a54f2c7SM Chetan Kumar 334*2a54f2c7SM Chetan Kumar wwan_unregister_ops(ipc_wwan->dev); 335*2a54f2c7SM Chetan Kumar 336*2a54f2c7SM Chetan Kumar for (if_id = 0; if_id < ARRAY_SIZE(ipc_wwan->sub_netlist); if_id++) { 337*2a54f2c7SM Chetan Kumar struct iosm_netdev_priv *priv; 338*2a54f2c7SM Chetan Kumar 339*2a54f2c7SM Chetan Kumar priv = rcu_access_pointer(ipc_wwan->sub_netlist[if_id]); 340*2a54f2c7SM Chetan Kumar if (!priv) 341*2a54f2c7SM Chetan Kumar continue; 342*2a54f2c7SM Chetan Kumar 343*2a54f2c7SM Chetan Kumar rtnl_lock(); 344*2a54f2c7SM Chetan Kumar ipc_wwan_dellink(ipc_wwan, priv->netdev, NULL); 345*2a54f2c7SM Chetan Kumar rtnl_unlock(); 346*2a54f2c7SM Chetan Kumar } 347*2a54f2c7SM Chetan Kumar 348*2a54f2c7SM Chetan Kumar mutex_destroy(&ipc_wwan->if_mutex); 349*2a54f2c7SM Chetan Kumar 350*2a54f2c7SM Chetan Kumar kfree(ipc_wwan); 351*2a54f2c7SM Chetan Kumar } 352