Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: iosm: net driver

1) Create net device & implement net operations for data/IP communication.
2) Bind IP Link to mux IP session for simultaneous IP traffic.

Signed-off-by: M Chetan Kumar <m.chetan.kumar@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

M Chetan Kumar and committed by
David S. Miller
2a54f2c7 110e6e02

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