162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 462306a36Sopenharmony_ci * Authors: Sjur Brendeland 562306a36Sopenharmony_ci * Daniel Martensson 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/if_ether.h> 1562306a36Sopenharmony_ci#include <linux/ip.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/sockios.h> 1862306a36Sopenharmony_ci#include <linux/caif/if_caif.h> 1962306a36Sopenharmony_ci#include <net/rtnetlink.h> 2062306a36Sopenharmony_ci#include <net/caif/caif_layer.h> 2162306a36Sopenharmony_ci#include <net/caif/cfpkt.h> 2262306a36Sopenharmony_ci#include <net/caif/caif_dev.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* GPRS PDP connection has MTU to 1500 */ 2562306a36Sopenharmony_ci#define GPRS_PDP_MTU 1500 2662306a36Sopenharmony_ci/* 5 sec. connect timeout */ 2762306a36Sopenharmony_ci#define CONNECT_TIMEOUT (5 * HZ) 2862306a36Sopenharmony_ci#define CAIF_NET_DEFAULT_QUEUE_LEN 500 2962306a36Sopenharmony_ci#define UNDEF_CONNID 0xffffffff 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/*This list is protected by the rtnl lock. */ 3262306a36Sopenharmony_cistatic LIST_HEAD(chnl_net_list); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("caif"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cienum caif_states { 3862306a36Sopenharmony_ci CAIF_CONNECTED = 1, 3962306a36Sopenharmony_ci CAIF_CONNECTING, 4062306a36Sopenharmony_ci CAIF_DISCONNECTED, 4162306a36Sopenharmony_ci CAIF_SHUTDOWN 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct chnl_net { 4562306a36Sopenharmony_ci struct cflayer chnl; 4662306a36Sopenharmony_ci struct caif_connect_request conn_req; 4762306a36Sopenharmony_ci struct list_head list_field; 4862306a36Sopenharmony_ci struct net_device *netdev; 4962306a36Sopenharmony_ci char name[256]; 5062306a36Sopenharmony_ci wait_queue_head_t netmgmt_wq; 5162306a36Sopenharmony_ci /* Flow status to remember and control the transmission. */ 5262306a36Sopenharmony_ci bool flowenabled; 5362306a36Sopenharmony_ci enum caif_states state; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct sk_buff *skb; 5962306a36Sopenharmony_ci struct chnl_net *priv; 6062306a36Sopenharmony_ci int pktlen; 6162306a36Sopenharmony_ci const u8 *ip_version; 6262306a36Sopenharmony_ci u8 buf; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci priv = container_of(layr, struct chnl_net, chnl); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci skb = (struct sk_buff *) cfpkt_tonative(pkt); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Get length of CAIF packet. */ 6962306a36Sopenharmony_ci pktlen = skb->len; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Pass some minimum information and 7262306a36Sopenharmony_ci * send the packet to the net stack. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci skb->dev = priv->netdev; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* check the version of IP */ 7762306a36Sopenharmony_ci ip_version = skb_header_pointer(skb, 0, 1, &buf); 7862306a36Sopenharmony_ci if (!ip_version) { 7962306a36Sopenharmony_ci kfree_skb(skb); 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci switch (*ip_version >> 4) { 8462306a36Sopenharmony_ci case 4: 8562306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci case 6: 8862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci default: 9162306a36Sopenharmony_ci kfree_skb(skb); 9262306a36Sopenharmony_ci priv->netdev->stats.rx_errors++; 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* If we change the header in loop mode, the checksum is corrupted. */ 9762306a36Sopenharmony_ci if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) 9862306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci netif_rx(skb); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Update statistics. */ 10562306a36Sopenharmony_ci priv->netdev->stats.rx_packets++; 10662306a36Sopenharmony_ci priv->netdev->stats.rx_bytes += pktlen; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int delete_device(struct chnl_net *dev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci ASSERT_RTNL(); 11462306a36Sopenharmony_ci if (dev->netdev) 11562306a36Sopenharmony_ci unregister_netdevice(dev->netdev); 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void close_work(struct work_struct *work) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct chnl_net *dev = NULL; 12262306a36Sopenharmony_ci struct list_head *list_node; 12362306a36Sopenharmony_ci struct list_head *_tmp; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci rtnl_lock(); 12662306a36Sopenharmony_ci list_for_each_safe(list_node, _tmp, &chnl_net_list) { 12762306a36Sopenharmony_ci dev = list_entry(list_node, struct chnl_net, list_field); 12862306a36Sopenharmony_ci if (dev->state == CAIF_SHUTDOWN) 12962306a36Sopenharmony_ci dev_close(dev->netdev); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci rtnl_unlock(); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_cistatic DECLARE_WORK(close_worker, close_work); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void chnl_hold(struct cflayer *lyr) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); 13862306a36Sopenharmony_ci dev_hold(priv->netdev); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void chnl_put(struct cflayer *lyr) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); 14462306a36Sopenharmony_ci dev_put(priv->netdev); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, 14862306a36Sopenharmony_ci int phyid) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); 15162306a36Sopenharmony_ci pr_debug("NET flowctrl func called flow: %s\n", 15262306a36Sopenharmony_ci flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : 15362306a36Sopenharmony_ci flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : 15462306a36Sopenharmony_ci flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : 15562306a36Sopenharmony_ci flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" : 15662306a36Sopenharmony_ci flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" : 15762306a36Sopenharmony_ci flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? 15862306a36Sopenharmony_ci "REMOTE_SHUTDOWN" : "UNKNOWN CTRL COMMAND"); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci switch (flow) { 16362306a36Sopenharmony_ci case CAIF_CTRLCMD_FLOW_OFF_IND: 16462306a36Sopenharmony_ci priv->flowenabled = false; 16562306a36Sopenharmony_ci netif_stop_queue(priv->netdev); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case CAIF_CTRLCMD_DEINIT_RSP: 16862306a36Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case CAIF_CTRLCMD_INIT_FAIL_RSP: 17162306a36Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 17262306a36Sopenharmony_ci wake_up_interruptible(&priv->netmgmt_wq); 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: 17562306a36Sopenharmony_ci priv->state = CAIF_SHUTDOWN; 17662306a36Sopenharmony_ci netif_tx_disable(priv->netdev); 17762306a36Sopenharmony_ci schedule_work(&close_worker); 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci case CAIF_CTRLCMD_FLOW_ON_IND: 18062306a36Sopenharmony_ci priv->flowenabled = true; 18162306a36Sopenharmony_ci netif_wake_queue(priv->netdev); 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case CAIF_CTRLCMD_INIT_RSP: 18462306a36Sopenharmony_ci caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put); 18562306a36Sopenharmony_ci priv->state = CAIF_CONNECTED; 18662306a36Sopenharmony_ci priv->flowenabled = true; 18762306a36Sopenharmony_ci netif_wake_queue(priv->netdev); 18862306a36Sopenharmony_ci wake_up_interruptible(&priv->netmgmt_wq); 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci default: 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb, 19662306a36Sopenharmony_ci struct net_device *dev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct chnl_net *priv; 19962306a36Sopenharmony_ci struct cfpkt *pkt = NULL; 20062306a36Sopenharmony_ci int len; 20162306a36Sopenharmony_ci int result = -1; 20262306a36Sopenharmony_ci /* Get our private data. */ 20362306a36Sopenharmony_ci priv = netdev_priv(dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (skb->len > priv->netdev->mtu) { 20662306a36Sopenharmony_ci pr_warn("Size of skb exceeded MTU\n"); 20762306a36Sopenharmony_ci kfree_skb(skb); 20862306a36Sopenharmony_ci dev->stats.tx_errors++; 20962306a36Sopenharmony_ci return NETDEV_TX_OK; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!priv->flowenabled) { 21362306a36Sopenharmony_ci pr_debug("dropping packets flow off\n"); 21462306a36Sopenharmony_ci kfree_skb(skb); 21562306a36Sopenharmony_ci dev->stats.tx_dropped++; 21662306a36Sopenharmony_ci return NETDEV_TX_OK; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) 22062306a36Sopenharmony_ci swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Store original SKB length. */ 22362306a36Sopenharmony_ci len = skb->len; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Send the packet down the stack. */ 22862306a36Sopenharmony_ci result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); 22962306a36Sopenharmony_ci if (result) { 23062306a36Sopenharmony_ci dev->stats.tx_dropped++; 23162306a36Sopenharmony_ci return NETDEV_TX_OK; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Update statistics. */ 23562306a36Sopenharmony_ci dev->stats.tx_packets++; 23662306a36Sopenharmony_ci dev->stats.tx_bytes += len; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return NETDEV_TX_OK; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int chnl_net_open(struct net_device *dev) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct chnl_net *priv = NULL; 24462306a36Sopenharmony_ci int result = -1; 24562306a36Sopenharmony_ci int llifindex, headroom, tailroom, mtu; 24662306a36Sopenharmony_ci struct net_device *lldev; 24762306a36Sopenharmony_ci ASSERT_RTNL(); 24862306a36Sopenharmony_ci priv = netdev_priv(dev); 24962306a36Sopenharmony_ci if (!priv) { 25062306a36Sopenharmony_ci pr_debug("chnl_net_open: no priv\n"); 25162306a36Sopenharmony_ci return -ENODEV; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (priv->state != CAIF_CONNECTING) { 25562306a36Sopenharmony_ci priv->state = CAIF_CONNECTING; 25662306a36Sopenharmony_ci result = caif_connect_client(dev_net(dev), &priv->conn_req, 25762306a36Sopenharmony_ci &priv->chnl, &llifindex, 25862306a36Sopenharmony_ci &headroom, &tailroom); 25962306a36Sopenharmony_ci if (result != 0) { 26062306a36Sopenharmony_ci pr_debug("err: " 26162306a36Sopenharmony_ci "Unable to register and open device," 26262306a36Sopenharmony_ci " Err:%d\n", 26362306a36Sopenharmony_ci result); 26462306a36Sopenharmony_ci goto error; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci lldev = __dev_get_by_index(dev_net(dev), llifindex); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (lldev == NULL) { 27062306a36Sopenharmony_ci pr_debug("no interface?\n"); 27162306a36Sopenharmony_ci result = -ENODEV; 27262306a36Sopenharmony_ci goto error; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci dev->needed_tailroom = tailroom + lldev->needed_tailroom; 27662306a36Sopenharmony_ci dev->hard_header_len = headroom + lldev->hard_header_len + 27762306a36Sopenharmony_ci lldev->needed_tailroom; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * MTU, head-room etc is not know before we have a 28162306a36Sopenharmony_ci * CAIF link layer device available. MTU calculation may 28262306a36Sopenharmony_ci * override initial RTNL configuration. 28362306a36Sopenharmony_ci * MTU is minimum of current mtu, link layer mtu pluss 28462306a36Sopenharmony_ci * CAIF head and tail, and PDP GPRS contexts max MTU. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom)); 28762306a36Sopenharmony_ci mtu = min_t(int, GPRS_PDP_MTU, mtu); 28862306a36Sopenharmony_ci dev_set_mtu(dev, mtu); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (mtu < 100) { 29162306a36Sopenharmony_ci pr_warn("CAIF Interface MTU too small (%d)\n", mtu); 29262306a36Sopenharmony_ci result = -ENODEV; 29362306a36Sopenharmony_ci goto error; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci rtnl_unlock(); /* Release RTNL lock during connect wait */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci result = wait_event_interruptible_timeout(priv->netmgmt_wq, 30062306a36Sopenharmony_ci priv->state != CAIF_CONNECTING, 30162306a36Sopenharmony_ci CONNECT_TIMEOUT); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci rtnl_lock(); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (result == -ERESTARTSYS) { 30662306a36Sopenharmony_ci pr_debug("wait_event_interruptible woken by a signal\n"); 30762306a36Sopenharmony_ci result = -ERESTARTSYS; 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (result == 0) { 31262306a36Sopenharmony_ci pr_debug("connect timeout\n"); 31362306a36Sopenharmony_ci result = -ETIMEDOUT; 31462306a36Sopenharmony_ci goto error; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (priv->state != CAIF_CONNECTED) { 31862306a36Sopenharmony_ci pr_debug("connect failed\n"); 31962306a36Sopenharmony_ci result = -ECONNREFUSED; 32062306a36Sopenharmony_ci goto error; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci pr_debug("CAIF Netdevice connected\n"); 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cierror: 32662306a36Sopenharmony_ci caif_disconnect_client(dev_net(dev), &priv->chnl); 32762306a36Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 32862306a36Sopenharmony_ci pr_debug("state disconnected\n"); 32962306a36Sopenharmony_ci return result; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic int chnl_net_stop(struct net_device *dev) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct chnl_net *priv; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ASSERT_RTNL(); 33862306a36Sopenharmony_ci priv = netdev_priv(dev); 33962306a36Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 34062306a36Sopenharmony_ci caif_disconnect_client(dev_net(dev), &priv->chnl); 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int chnl_net_init(struct net_device *dev) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct chnl_net *priv; 34762306a36Sopenharmony_ci ASSERT_RTNL(); 34862306a36Sopenharmony_ci priv = netdev_priv(dev); 34962306a36Sopenharmony_ci strncpy(priv->name, dev->name, sizeof(priv->name)); 35062306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->list_field); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void chnl_net_uninit(struct net_device *dev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct chnl_net *priv; 35762306a36Sopenharmony_ci ASSERT_RTNL(); 35862306a36Sopenharmony_ci priv = netdev_priv(dev); 35962306a36Sopenharmony_ci list_del_init(&priv->list_field); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 36362306a36Sopenharmony_ci .ndo_open = chnl_net_open, 36462306a36Sopenharmony_ci .ndo_stop = chnl_net_stop, 36562306a36Sopenharmony_ci .ndo_init = chnl_net_init, 36662306a36Sopenharmony_ci .ndo_uninit = chnl_net_uninit, 36762306a36Sopenharmony_ci .ndo_start_xmit = chnl_net_start_xmit, 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void chnl_net_destructor(struct net_device *dev) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct chnl_net *priv = netdev_priv(dev); 37362306a36Sopenharmony_ci caif_free_client(&priv->chnl); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void ipcaif_net_setup(struct net_device *dev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct chnl_net *priv; 37962306a36Sopenharmony_ci dev->netdev_ops = &netdev_ops; 38062306a36Sopenharmony_ci dev->needs_free_netdev = true; 38162306a36Sopenharmony_ci dev->priv_destructor = chnl_net_destructor; 38262306a36Sopenharmony_ci dev->flags |= IFF_NOARP; 38362306a36Sopenharmony_ci dev->flags |= IFF_POINTOPOINT; 38462306a36Sopenharmony_ci dev->mtu = GPRS_PDP_MTU; 38562306a36Sopenharmony_ci dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci priv = netdev_priv(dev); 38862306a36Sopenharmony_ci priv->chnl.receive = chnl_recv_cb; 38962306a36Sopenharmony_ci priv->chnl.ctrlcmd = chnl_flowctrl_cb; 39062306a36Sopenharmony_ci priv->netdev = dev; 39162306a36Sopenharmony_ci priv->conn_req.protocol = CAIFPROTO_DATAGRAM; 39262306a36Sopenharmony_ci priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; 39362306a36Sopenharmony_ci priv->conn_req.priority = CAIF_PRIO_LOW; 39462306a36Sopenharmony_ci /* Insert illegal value */ 39562306a36Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID; 39662306a36Sopenharmony_ci priv->flowenabled = false; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci init_waitqueue_head(&priv->netmgmt_wq); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct chnl_net *priv; 40562306a36Sopenharmony_ci u8 loop; 40662306a36Sopenharmony_ci priv = netdev_priv(dev); 40762306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID, 40862306a36Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id) || 40962306a36Sopenharmony_ci nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID, 41062306a36Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id)) 41162306a36Sopenharmony_ci goto nla_put_failure; 41262306a36Sopenharmony_ci loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; 41362306a36Sopenharmony_ci if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop)) 41462306a36Sopenharmony_ci goto nla_put_failure; 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_cinla_put_failure: 41762306a36Sopenharmony_ci return -EMSGSIZE; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void caif_netlink_parms(struct nlattr *data[], 42262306a36Sopenharmony_ci struct caif_connect_request *conn_req) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci if (!data) { 42562306a36Sopenharmony_ci pr_warn("no params data found\n"); 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (data[IFLA_CAIF_IPV4_CONNID]) 42962306a36Sopenharmony_ci conn_req->sockaddr.u.dgm.connection_id = 43062306a36Sopenharmony_ci nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]); 43162306a36Sopenharmony_ci if (data[IFLA_CAIF_IPV6_CONNID]) 43262306a36Sopenharmony_ci conn_req->sockaddr.u.dgm.connection_id = 43362306a36Sopenharmony_ci nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]); 43462306a36Sopenharmony_ci if (data[IFLA_CAIF_LOOPBACK]) { 43562306a36Sopenharmony_ci if (nla_get_u8(data[IFLA_CAIF_LOOPBACK])) 43662306a36Sopenharmony_ci conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP; 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci conn_req->protocol = CAIFPROTO_DATAGRAM; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int ipcaif_newlink(struct net *src_net, struct net_device *dev, 44362306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 44462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int ret; 44762306a36Sopenharmony_ci struct chnl_net *caifdev; 44862306a36Sopenharmony_ci ASSERT_RTNL(); 44962306a36Sopenharmony_ci caifdev = netdev_priv(dev); 45062306a36Sopenharmony_ci caif_netlink_parms(data, &caifdev->conn_req); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = register_netdevice(dev); 45362306a36Sopenharmony_ci if (ret) 45462306a36Sopenharmony_ci pr_warn("device rtml registration failed\n"); 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci list_add(&caifdev->list_field, &chnl_net_list); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Use ifindex as connection id, and use loopback channel default. */ 45962306a36Sopenharmony_ci if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) { 46062306a36Sopenharmony_ci caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; 46162306a36Sopenharmony_ci caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci return ret; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], 46762306a36Sopenharmony_ci struct nlattr *data[], 46862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct chnl_net *caifdev; 47162306a36Sopenharmony_ci ASSERT_RTNL(); 47262306a36Sopenharmony_ci caifdev = netdev_priv(dev); 47362306a36Sopenharmony_ci caif_netlink_parms(data, &caifdev->conn_req); 47462306a36Sopenharmony_ci netdev_state_change(dev); 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic size_t ipcaif_get_size(const struct net_device *dev) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci return 48162306a36Sopenharmony_ci /* IFLA_CAIF_IPV4_CONNID */ 48262306a36Sopenharmony_ci nla_total_size(4) + 48362306a36Sopenharmony_ci /* IFLA_CAIF_IPV6_CONNID */ 48462306a36Sopenharmony_ci nla_total_size(4) + 48562306a36Sopenharmony_ci /* IFLA_CAIF_LOOPBACK */ 48662306a36Sopenharmony_ci nla_total_size(2) + 48762306a36Sopenharmony_ci 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = { 49162306a36Sopenharmony_ci [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 }, 49262306a36Sopenharmony_ci [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 }, 49362306a36Sopenharmony_ci [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 } 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct rtnl_link_ops ipcaif_link_ops __read_mostly = { 49862306a36Sopenharmony_ci .kind = "caif", 49962306a36Sopenharmony_ci .priv_size = sizeof(struct chnl_net), 50062306a36Sopenharmony_ci .setup = ipcaif_net_setup, 50162306a36Sopenharmony_ci .maxtype = IFLA_CAIF_MAX, 50262306a36Sopenharmony_ci .policy = ipcaif_policy, 50362306a36Sopenharmony_ci .newlink = ipcaif_newlink, 50462306a36Sopenharmony_ci .changelink = ipcaif_changelink, 50562306a36Sopenharmony_ci .get_size = ipcaif_get_size, 50662306a36Sopenharmony_ci .fill_info = ipcaif_fill_info, 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int __init chnl_init_module(void) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci return rtnl_link_register(&ipcaif_link_ops); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void __exit chnl_exit_module(void) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct chnl_net *dev = NULL; 51862306a36Sopenharmony_ci struct list_head *list_node; 51962306a36Sopenharmony_ci struct list_head *_tmp; 52062306a36Sopenharmony_ci rtnl_link_unregister(&ipcaif_link_ops); 52162306a36Sopenharmony_ci rtnl_lock(); 52262306a36Sopenharmony_ci list_for_each_safe(list_node, _tmp, &chnl_net_list) { 52362306a36Sopenharmony_ci dev = list_entry(list_node, struct chnl_net, list_field); 52462306a36Sopenharmony_ci list_del_init(list_node); 52562306a36Sopenharmony_ci delete_device(dev); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci rtnl_unlock(); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cimodule_init(chnl_init_module); 53162306a36Sopenharmony_cimodule_exit(chnl_exit_module); 532