18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 48c2ecf20Sopenharmony_ci * Authors: Sjur Brendeland 58c2ecf20Sopenharmony_ci * Daniel Martensson 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 158c2ecf20Sopenharmony_ci#include <linux/ip.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/sockios.h> 188c2ecf20Sopenharmony_ci#include <linux/caif/if_caif.h> 198c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 208c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h> 218c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h> 228c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* GPRS PDP connection has MTU to 1500 */ 258c2ecf20Sopenharmony_ci#define GPRS_PDP_MTU 1500 268c2ecf20Sopenharmony_ci/* 5 sec. connect timeout */ 278c2ecf20Sopenharmony_ci#define CONNECT_TIMEOUT (5 * HZ) 288c2ecf20Sopenharmony_ci#define CAIF_NET_DEFAULT_QUEUE_LEN 500 298c2ecf20Sopenharmony_ci#define UNDEF_CONNID 0xffffffff 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/*This list is protected by the rtnl lock. */ 328c2ecf20Sopenharmony_cistatic LIST_HEAD(chnl_net_list); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("caif"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cienum caif_states { 388c2ecf20Sopenharmony_ci CAIF_CONNECTED = 1, 398c2ecf20Sopenharmony_ci CAIF_CONNECTING, 408c2ecf20Sopenharmony_ci CAIF_DISCONNECTED, 418c2ecf20Sopenharmony_ci CAIF_SHUTDOWN 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct chnl_net { 458c2ecf20Sopenharmony_ci struct cflayer chnl; 468c2ecf20Sopenharmony_ci struct caif_connect_request conn_req; 478c2ecf20Sopenharmony_ci struct list_head list_field; 488c2ecf20Sopenharmony_ci struct net_device *netdev; 498c2ecf20Sopenharmony_ci char name[256]; 508c2ecf20Sopenharmony_ci wait_queue_head_t netmgmt_wq; 518c2ecf20Sopenharmony_ci /* Flow status to remember and control the transmission. */ 528c2ecf20Sopenharmony_ci bool flowenabled; 538c2ecf20Sopenharmony_ci enum caif_states state; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct sk_buff *skb; 598c2ecf20Sopenharmony_ci struct chnl_net *priv; 608c2ecf20Sopenharmony_ci int pktlen; 618c2ecf20Sopenharmony_ci const u8 *ip_version; 628c2ecf20Sopenharmony_ci u8 buf; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci priv = container_of(layr, struct chnl_net, chnl); 658c2ecf20Sopenharmony_ci if (!priv) 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci skb = (struct sk_buff *) cfpkt_tonative(pkt); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Get length of CAIF packet. */ 718c2ecf20Sopenharmony_ci pktlen = skb->len; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Pass some minimum information and 748c2ecf20Sopenharmony_ci * send the packet to the net stack. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci skb->dev = priv->netdev; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* check the version of IP */ 798c2ecf20Sopenharmony_ci ip_version = skb_header_pointer(skb, 0, 1, &buf); 808c2ecf20Sopenharmony_ci if (!ip_version) { 818c2ecf20Sopenharmony_ci kfree_skb(skb); 828c2ecf20Sopenharmony_ci return -EINVAL; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (*ip_version >> 4) { 868c2ecf20Sopenharmony_ci case 4: 878c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci case 6: 908c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci default: 938c2ecf20Sopenharmony_ci kfree_skb(skb); 948c2ecf20Sopenharmony_ci priv->netdev->stats.rx_errors++; 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* If we change the header in loop mode, the checksum is corrupted. */ 998c2ecf20Sopenharmony_ci if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) 1008c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 1018c2ecf20Sopenharmony_ci else 1028c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (in_interrupt()) 1058c2ecf20Sopenharmony_ci netif_rx(skb); 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci netif_rx_ni(skb); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Update statistics. */ 1108c2ecf20Sopenharmony_ci priv->netdev->stats.rx_packets++; 1118c2ecf20Sopenharmony_ci priv->netdev->stats.rx_bytes += pktlen; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int delete_device(struct chnl_net *dev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1198c2ecf20Sopenharmony_ci if (dev->netdev) 1208c2ecf20Sopenharmony_ci unregister_netdevice(dev->netdev); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void close_work(struct work_struct *work) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct chnl_net *dev = NULL; 1278c2ecf20Sopenharmony_ci struct list_head *list_node; 1288c2ecf20Sopenharmony_ci struct list_head *_tmp; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rtnl_lock(); 1318c2ecf20Sopenharmony_ci list_for_each_safe(list_node, _tmp, &chnl_net_list) { 1328c2ecf20Sopenharmony_ci dev = list_entry(list_node, struct chnl_net, list_field); 1338c2ecf20Sopenharmony_ci if (dev->state == CAIF_SHUTDOWN) 1348c2ecf20Sopenharmony_ci dev_close(dev->netdev); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci rtnl_unlock(); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_cistatic DECLARE_WORK(close_worker, close_work); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void chnl_hold(struct cflayer *lyr) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); 1438c2ecf20Sopenharmony_ci dev_hold(priv->netdev); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void chnl_put(struct cflayer *lyr) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl); 1498c2ecf20Sopenharmony_ci dev_put(priv->netdev); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, 1538c2ecf20Sopenharmony_ci int phyid) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); 1568c2ecf20Sopenharmony_ci pr_debug("NET flowctrl func called flow: %s\n", 1578c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : 1588c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : 1598c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : 1608c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" : 1618c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" : 1628c2ecf20Sopenharmony_ci flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? 1638c2ecf20Sopenharmony_ci "REMOTE_SHUTDOWN" : "UNKNOWN CTRL COMMAND"); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (flow) { 1688c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_FLOW_OFF_IND: 1698c2ecf20Sopenharmony_ci priv->flowenabled = false; 1708c2ecf20Sopenharmony_ci netif_stop_queue(priv->netdev); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_DEINIT_RSP: 1738c2ecf20Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_INIT_FAIL_RSP: 1768c2ecf20Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 1778c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->netmgmt_wq); 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: 1808c2ecf20Sopenharmony_ci priv->state = CAIF_SHUTDOWN; 1818c2ecf20Sopenharmony_ci netif_tx_disable(priv->netdev); 1828c2ecf20Sopenharmony_ci schedule_work(&close_worker); 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_FLOW_ON_IND: 1858c2ecf20Sopenharmony_ci priv->flowenabled = true; 1868c2ecf20Sopenharmony_ci netif_wake_queue(priv->netdev); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case CAIF_CTRLCMD_INIT_RSP: 1898c2ecf20Sopenharmony_ci caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put); 1908c2ecf20Sopenharmony_ci priv->state = CAIF_CONNECTED; 1918c2ecf20Sopenharmony_ci priv->flowenabled = true; 1928c2ecf20Sopenharmony_ci netif_wake_queue(priv->netdev); 1938c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->netmgmt_wq); 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci default: 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb, 2018c2ecf20Sopenharmony_ci struct net_device *dev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct chnl_net *priv; 2048c2ecf20Sopenharmony_ci struct cfpkt *pkt = NULL; 2058c2ecf20Sopenharmony_ci int len; 2068c2ecf20Sopenharmony_ci int result = -1; 2078c2ecf20Sopenharmony_ci /* Get our private data. */ 2088c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (skb->len > priv->netdev->mtu) { 2118c2ecf20Sopenharmony_ci pr_warn("Size of skb exceeded MTU\n"); 2128c2ecf20Sopenharmony_ci kfree_skb(skb); 2138c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 2148c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!priv->flowenabled) { 2188c2ecf20Sopenharmony_ci pr_debug("dropping packets flow off\n"); 2198c2ecf20Sopenharmony_ci kfree_skb(skb); 2208c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 2218c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP) 2258c2ecf20Sopenharmony_ci swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Store original SKB length. */ 2288c2ecf20Sopenharmony_ci len = skb->len; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Send the packet down the stack. */ 2338c2ecf20Sopenharmony_ci result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); 2348c2ecf20Sopenharmony_ci if (result) { 2358c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 2368c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Update statistics. */ 2408c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 2418c2ecf20Sopenharmony_ci dev->stats.tx_bytes += len; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int chnl_net_open(struct net_device *dev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct chnl_net *priv = NULL; 2498c2ecf20Sopenharmony_ci int result = -1; 2508c2ecf20Sopenharmony_ci int llifindex, headroom, tailroom, mtu; 2518c2ecf20Sopenharmony_ci struct net_device *lldev; 2528c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2538c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2548c2ecf20Sopenharmony_ci if (!priv) { 2558c2ecf20Sopenharmony_ci pr_debug("chnl_net_open: no priv\n"); 2568c2ecf20Sopenharmony_ci return -ENODEV; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (priv->state != CAIF_CONNECTING) { 2608c2ecf20Sopenharmony_ci priv->state = CAIF_CONNECTING; 2618c2ecf20Sopenharmony_ci result = caif_connect_client(dev_net(dev), &priv->conn_req, 2628c2ecf20Sopenharmony_ci &priv->chnl, &llifindex, 2638c2ecf20Sopenharmony_ci &headroom, &tailroom); 2648c2ecf20Sopenharmony_ci if (result != 0) { 2658c2ecf20Sopenharmony_ci pr_debug("err: " 2668c2ecf20Sopenharmony_ci "Unable to register and open device," 2678c2ecf20Sopenharmony_ci " Err:%d\n", 2688c2ecf20Sopenharmony_ci result); 2698c2ecf20Sopenharmony_ci goto error; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci lldev = __dev_get_by_index(dev_net(dev), llifindex); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (lldev == NULL) { 2758c2ecf20Sopenharmony_ci pr_debug("no interface?\n"); 2768c2ecf20Sopenharmony_ci result = -ENODEV; 2778c2ecf20Sopenharmony_ci goto error; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dev->needed_tailroom = tailroom + lldev->needed_tailroom; 2818c2ecf20Sopenharmony_ci dev->hard_header_len = headroom + lldev->hard_header_len + 2828c2ecf20Sopenharmony_ci lldev->needed_tailroom; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * MTU, head-room etc is not know before we have a 2868c2ecf20Sopenharmony_ci * CAIF link layer device available. MTU calculation may 2878c2ecf20Sopenharmony_ci * override initial RTNL configuration. 2888c2ecf20Sopenharmony_ci * MTU is minimum of current mtu, link layer mtu pluss 2898c2ecf20Sopenharmony_ci * CAIF head and tail, and PDP GPRS contexts max MTU. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom)); 2928c2ecf20Sopenharmony_ci mtu = min_t(int, GPRS_PDP_MTU, mtu); 2938c2ecf20Sopenharmony_ci dev_set_mtu(dev, mtu); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (mtu < 100) { 2968c2ecf20Sopenharmony_ci pr_warn("CAIF Interface MTU too small (%d)\n", mtu); 2978c2ecf20Sopenharmony_ci result = -ENODEV; 2988c2ecf20Sopenharmony_ci goto error; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci rtnl_unlock(); /* Release RTNL lock during connect wait */ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci result = wait_event_interruptible_timeout(priv->netmgmt_wq, 3058c2ecf20Sopenharmony_ci priv->state != CAIF_CONNECTING, 3068c2ecf20Sopenharmony_ci CONNECT_TIMEOUT); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci rtnl_lock(); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (result == -ERESTARTSYS) { 3118c2ecf20Sopenharmony_ci pr_debug("wait_event_interruptible woken by a signal\n"); 3128c2ecf20Sopenharmony_ci result = -ERESTARTSYS; 3138c2ecf20Sopenharmony_ci goto error; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (result == 0) { 3178c2ecf20Sopenharmony_ci pr_debug("connect timeout\n"); 3188c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 3198c2ecf20Sopenharmony_ci goto error; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (priv->state != CAIF_CONNECTED) { 3238c2ecf20Sopenharmony_ci pr_debug("connect failed\n"); 3248c2ecf20Sopenharmony_ci result = -ECONNREFUSED; 3258c2ecf20Sopenharmony_ci goto error; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci pr_debug("CAIF Netdevice connected\n"); 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cierror: 3318c2ecf20Sopenharmony_ci caif_disconnect_client(dev_net(dev), &priv->chnl); 3328c2ecf20Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 3338c2ecf20Sopenharmony_ci pr_debug("state disconnected\n"); 3348c2ecf20Sopenharmony_ci return result; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int chnl_net_stop(struct net_device *dev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct chnl_net *priv; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3438c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3448c2ecf20Sopenharmony_ci priv->state = CAIF_DISCONNECTED; 3458c2ecf20Sopenharmony_ci caif_disconnect_client(dev_net(dev), &priv->chnl); 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int chnl_net_init(struct net_device *dev) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct chnl_net *priv; 3528c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3538c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3548c2ecf20Sopenharmony_ci strncpy(priv->name, dev->name, sizeof(priv->name)); 3558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->list_field); 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void chnl_net_uninit(struct net_device *dev) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct chnl_net *priv; 3628c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3638c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3648c2ecf20Sopenharmony_ci list_del_init(&priv->list_field); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 3688c2ecf20Sopenharmony_ci .ndo_open = chnl_net_open, 3698c2ecf20Sopenharmony_ci .ndo_stop = chnl_net_stop, 3708c2ecf20Sopenharmony_ci .ndo_init = chnl_net_init, 3718c2ecf20Sopenharmony_ci .ndo_uninit = chnl_net_uninit, 3728c2ecf20Sopenharmony_ci .ndo_start_xmit = chnl_net_start_xmit, 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void chnl_net_destructor(struct net_device *dev) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct chnl_net *priv = netdev_priv(dev); 3788c2ecf20Sopenharmony_ci caif_free_client(&priv->chnl); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic void ipcaif_net_setup(struct net_device *dev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct chnl_net *priv; 3848c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 3858c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 3868c2ecf20Sopenharmony_ci dev->priv_destructor = chnl_net_destructor; 3878c2ecf20Sopenharmony_ci dev->flags |= IFF_NOARP; 3888c2ecf20Sopenharmony_ci dev->flags |= IFF_POINTOPOINT; 3898c2ecf20Sopenharmony_ci dev->mtu = GPRS_PDP_MTU; 3908c2ecf20Sopenharmony_ci dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3938c2ecf20Sopenharmony_ci priv->chnl.receive = chnl_recv_cb; 3948c2ecf20Sopenharmony_ci priv->chnl.ctrlcmd = chnl_flowctrl_cb; 3958c2ecf20Sopenharmony_ci priv->netdev = dev; 3968c2ecf20Sopenharmony_ci priv->conn_req.protocol = CAIFPROTO_DATAGRAM; 3978c2ecf20Sopenharmony_ci priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; 3988c2ecf20Sopenharmony_ci priv->conn_req.priority = CAIF_PRIO_LOW; 3998c2ecf20Sopenharmony_ci /* Insert illegal value */ 4008c2ecf20Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID; 4018c2ecf20Sopenharmony_ci priv->flowenabled = false; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->netmgmt_wq); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct chnl_net *priv; 4108c2ecf20Sopenharmony_ci u8 loop; 4118c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 4128c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID, 4138c2ecf20Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id) || 4148c2ecf20Sopenharmony_ci nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID, 4158c2ecf20Sopenharmony_ci priv->conn_req.sockaddr.u.dgm.connection_id)) 4168c2ecf20Sopenharmony_ci goto nla_put_failure; 4178c2ecf20Sopenharmony_ci loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP; 4188c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop)) 4198c2ecf20Sopenharmony_ci goto nla_put_failure; 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_cinla_put_failure: 4228c2ecf20Sopenharmony_ci return -EMSGSIZE; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void caif_netlink_parms(struct nlattr *data[], 4278c2ecf20Sopenharmony_ci struct caif_connect_request *conn_req) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci if (!data) { 4308c2ecf20Sopenharmony_ci pr_warn("no params data found\n"); 4318c2ecf20Sopenharmony_ci return; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci if (data[IFLA_CAIF_IPV4_CONNID]) 4348c2ecf20Sopenharmony_ci conn_req->sockaddr.u.dgm.connection_id = 4358c2ecf20Sopenharmony_ci nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]); 4368c2ecf20Sopenharmony_ci if (data[IFLA_CAIF_IPV6_CONNID]) 4378c2ecf20Sopenharmony_ci conn_req->sockaddr.u.dgm.connection_id = 4388c2ecf20Sopenharmony_ci nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]); 4398c2ecf20Sopenharmony_ci if (data[IFLA_CAIF_LOOPBACK]) { 4408c2ecf20Sopenharmony_ci if (nla_get_u8(data[IFLA_CAIF_LOOPBACK])) 4418c2ecf20Sopenharmony_ci conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP; 4428c2ecf20Sopenharmony_ci else 4438c2ecf20Sopenharmony_ci conn_req->protocol = CAIFPROTO_DATAGRAM; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int ipcaif_newlink(struct net *src_net, struct net_device *dev, 4488c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 4498c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci struct chnl_net *caifdev; 4538c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4548c2ecf20Sopenharmony_ci caifdev = netdev_priv(dev); 4558c2ecf20Sopenharmony_ci caif_netlink_parms(data, &caifdev->conn_req); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ret = register_netdevice(dev); 4588c2ecf20Sopenharmony_ci if (ret) 4598c2ecf20Sopenharmony_ci pr_warn("device rtml registration failed\n"); 4608c2ecf20Sopenharmony_ci else 4618c2ecf20Sopenharmony_ci list_add(&caifdev->list_field, &chnl_net_list); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Use ifindex as connection id, and use loopback channel default. */ 4648c2ecf20Sopenharmony_ci if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) { 4658c2ecf20Sopenharmony_ci caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex; 4668c2ecf20Sopenharmony_ci caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[], 4728c2ecf20Sopenharmony_ci struct nlattr *data[], 4738c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct chnl_net *caifdev; 4768c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4778c2ecf20Sopenharmony_ci caifdev = netdev_priv(dev); 4788c2ecf20Sopenharmony_ci caif_netlink_parms(data, &caifdev->conn_req); 4798c2ecf20Sopenharmony_ci netdev_state_change(dev); 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic size_t ipcaif_get_size(const struct net_device *dev) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci return 4868c2ecf20Sopenharmony_ci /* IFLA_CAIF_IPV4_CONNID */ 4878c2ecf20Sopenharmony_ci nla_total_size(4) + 4888c2ecf20Sopenharmony_ci /* IFLA_CAIF_IPV6_CONNID */ 4898c2ecf20Sopenharmony_ci nla_total_size(4) + 4908c2ecf20Sopenharmony_ci /* IFLA_CAIF_LOOPBACK */ 4918c2ecf20Sopenharmony_ci nla_total_size(2) + 4928c2ecf20Sopenharmony_ci 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = { 4968c2ecf20Sopenharmony_ci [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 }, 4978c2ecf20Sopenharmony_ci [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 }, 4988c2ecf20Sopenharmony_ci [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 } 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ipcaif_link_ops __read_mostly = { 5038c2ecf20Sopenharmony_ci .kind = "caif", 5048c2ecf20Sopenharmony_ci .priv_size = sizeof(struct chnl_net), 5058c2ecf20Sopenharmony_ci .setup = ipcaif_net_setup, 5068c2ecf20Sopenharmony_ci .maxtype = IFLA_CAIF_MAX, 5078c2ecf20Sopenharmony_ci .policy = ipcaif_policy, 5088c2ecf20Sopenharmony_ci .newlink = ipcaif_newlink, 5098c2ecf20Sopenharmony_ci .changelink = ipcaif_changelink, 5108c2ecf20Sopenharmony_ci .get_size = ipcaif_get_size, 5118c2ecf20Sopenharmony_ci .fill_info = ipcaif_fill_info, 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int __init chnl_init_module(void) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci return rtnl_link_register(&ipcaif_link_ops); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void __exit chnl_exit_module(void) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct chnl_net *dev = NULL; 5238c2ecf20Sopenharmony_ci struct list_head *list_node; 5248c2ecf20Sopenharmony_ci struct list_head *_tmp; 5258c2ecf20Sopenharmony_ci rtnl_link_unregister(&ipcaif_link_ops); 5268c2ecf20Sopenharmony_ci rtnl_lock(); 5278c2ecf20Sopenharmony_ci list_for_each_safe(list_node, _tmp, &chnl_net_list) { 5288c2ecf20Sopenharmony_ci dev = list_entry(list_node, struct chnl_net, list_field); 5298c2ecf20Sopenharmony_ci list_del_init(list_node); 5308c2ecf20Sopenharmony_ci delete_device(dev); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci rtnl_unlock(); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cimodule_init(chnl_init_module); 5368c2ecf20Sopenharmony_cimodule_exit(chnl_exit_module); 537