xref: /linux/drivers/net/wwan/iosm/iosm_ipc_wwan.c (revision 2a54f2c7793409736f2e5ea101e050b3f1997088)
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