18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. 48c2ecf20Sopenharmony_ci * All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "cfg80211.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cistruct wilc_wfi_radiotap_hdr { 108c2ecf20Sopenharmony_ci struct ieee80211_radiotap_header hdr; 118c2ecf20Sopenharmony_ci u8 rate; 128c2ecf20Sopenharmony_ci} __packed; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct wilc_wfi_radiotap_cb_hdr { 158c2ecf20Sopenharmony_ci struct ieee80211_radiotap_header hdr; 168c2ecf20Sopenharmony_ci u8 rate; 178c2ecf20Sopenharmony_ci u8 dump; 188c2ecf20Sopenharmony_ci u16 tx_flags; 198c2ecf20Sopenharmony_ci} __packed; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define TX_RADIOTAP_PRESENT ((1 << IEEE80211_RADIOTAP_RATE) | \ 228c2ecf20Sopenharmony_ci (1 << IEEE80211_RADIOTAP_TX_FLAGS)) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_civoid wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci u32 header, pkt_offset; 278c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 288c2ecf20Sopenharmony_ci struct wilc_wfi_radiotap_hdr *hdr; 298c2ecf20Sopenharmony_ci struct wilc_wfi_radiotap_cb_hdr *cb_hdr; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (!mon_dev) 328c2ecf20Sopenharmony_ci return; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!netif_running(mon_dev)) 358c2ecf20Sopenharmony_ci return; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Get WILC header */ 388c2ecf20Sopenharmony_ci header = get_unaligned_le32(buff - HOST_HDR_OFFSET); 398c2ecf20Sopenharmony_ci /* 408c2ecf20Sopenharmony_ci * The packet offset field contain info about what type of management 418c2ecf20Sopenharmony_ci * the frame we are dealing with and ack status 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (pkt_offset & IS_MANAGMEMENT_CALLBACK) { 468c2ecf20Sopenharmony_ci /* hostapd callback mgmt frame */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci skb = dev_alloc_skb(size + sizeof(*cb_hdr)); 498c2ecf20Sopenharmony_ci if (!skb) 508c2ecf20Sopenharmony_ci return; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci skb_put_data(skb, buff, size); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci cb_hdr = skb_push(skb, sizeof(*cb_hdr)); 558c2ecf20Sopenharmony_ci memset(cb_hdr, 0, sizeof(*cb_hdr)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci cb_hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci cb_hdr->hdr.it_len = cpu_to_le16(sizeof(*cb_hdr)); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci cb_hdr->hdr.it_present = cpu_to_le32(TX_RADIOTAP_PRESENT); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci cb_hdr->rate = 5; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (pkt_offset & IS_MGMT_STATUS_SUCCES) { 668c2ecf20Sopenharmony_ci /* success */ 678c2ecf20Sopenharmony_ci cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_RTS; 688c2ecf20Sopenharmony_ci } else { 698c2ecf20Sopenharmony_ci cb_hdr->tx_flags = IEEE80211_RADIOTAP_F_TX_FAIL; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci skb = dev_alloc_skb(size + sizeof(*hdr)); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!skb) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci skb_put_data(skb, buff, size); 798c2ecf20Sopenharmony_ci hdr = skb_push(skb, sizeof(*hdr)); 808c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(struct wilc_wfi_radiotap_hdr)); 818c2ecf20Sopenharmony_ci hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */ 828c2ecf20Sopenharmony_ci hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); 838c2ecf20Sopenharmony_ci hdr->hdr.it_present = cpu_to_le32 848c2ecf20Sopenharmony_ci (1 << IEEE80211_RADIOTAP_RATE); 858c2ecf20Sopenharmony_ci hdr->rate = 5; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci skb->dev = mon_dev; 898c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 908c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 918c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 928c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 938c2ecf20Sopenharmony_ci memset(skb->cb, 0, sizeof(skb->cb)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci netif_rx(skb); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistruct tx_complete_mon_data { 998c2ecf20Sopenharmony_ci int size; 1008c2ecf20Sopenharmony_ci void *buff; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void mgmt_tx_complete(void *priv, int status) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct tx_complete_mon_data *pv_data = priv; 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * in case of fully hosting mode, the freeing will be done 1088c2ecf20Sopenharmony_ci * in response to the cfg packet 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci kfree(pv_data->buff); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci kfree(pv_data); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct tx_complete_mon_data *mgmt_tx = NULL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!dev) 1208c2ecf20Sopenharmony_ci return -EFAULT; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci netif_stop_queue(dev); 1238c2ecf20Sopenharmony_ci mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_ATOMIC); 1248c2ecf20Sopenharmony_ci if (!mgmt_tx) 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mgmt_tx->buff = kmemdup(buf, len, GFP_ATOMIC); 1288c2ecf20Sopenharmony_ci if (!mgmt_tx->buff) { 1298c2ecf20Sopenharmony_ci kfree(mgmt_tx); 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci mgmt_tx->size = len; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci wilc_wlan_txq_add_mgmt_pkt(dev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size, 1368c2ecf20Sopenharmony_ci mgmt_tx_complete); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci netif_wake_queue(dev); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic netdev_tx_t wilc_wfi_mon_xmit(struct sk_buff *skb, 1438c2ecf20Sopenharmony_ci struct net_device *dev) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u32 rtap_len, ret = 0; 1468c2ecf20Sopenharmony_ci struct wilc_wfi_mon_priv *mon_priv; 1478c2ecf20Sopenharmony_ci struct sk_buff *skb2; 1488c2ecf20Sopenharmony_ci struct wilc_wfi_radiotap_cb_hdr *cb_hdr; 1498c2ecf20Sopenharmony_ci u8 srcadd[ETH_ALEN]; 1508c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci mon_priv = netdev_priv(dev); 1538c2ecf20Sopenharmony_ci if (!mon_priv) 1548c2ecf20Sopenharmony_ci return -EFAULT; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci rtap_len = ieee80211_get_radiotap_len(skb->data); 1578c2ecf20Sopenharmony_ci if (skb->len < rtap_len) 1588c2ecf20Sopenharmony_ci return -1; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci skb_pull(skb, rtap_len); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (skb->data[0] == 0xc0 && is_broadcast_ether_addr(&skb->data[4])) { 1638c2ecf20Sopenharmony_ci skb2 = dev_alloc_skb(skb->len + sizeof(*cb_hdr)); 1648c2ecf20Sopenharmony_ci if (!skb2) 1658c2ecf20Sopenharmony_ci return -ENOMEM; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci skb_put_data(skb2, skb->data, skb->len); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci cb_hdr = skb_push(skb2, sizeof(*cb_hdr)); 1708c2ecf20Sopenharmony_ci memset(cb_hdr, 0, sizeof(struct wilc_wfi_radiotap_cb_hdr)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci cb_hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci cb_hdr->hdr.it_len = cpu_to_le16(sizeof(*cb_hdr)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci cb_hdr->hdr.it_present = cpu_to_le32(TX_RADIOTAP_PRESENT); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci cb_hdr->rate = 5; 1798c2ecf20Sopenharmony_ci cb_hdr->tx_flags = 0x0004; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci skb2->dev = dev; 1828c2ecf20Sopenharmony_ci skb_reset_mac_header(skb2); 1838c2ecf20Sopenharmony_ci skb2->ip_summed = CHECKSUM_UNNECESSARY; 1848c2ecf20Sopenharmony_ci skb2->pkt_type = PACKET_OTHERHOST; 1858c2ecf20Sopenharmony_ci skb2->protocol = htons(ETH_P_802_2); 1868c2ecf20Sopenharmony_ci memset(skb2->cb, 0, sizeof(skb2->cb)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci netif_rx(skb2); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci skb->dev = mon_priv->real_ndev; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ether_addr_copy(srcadd, &skb->data[10]); 1958c2ecf20Sopenharmony_ci ether_addr_copy(bssid, &skb->data[16]); 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * Identify if data or mgmt packet, if source address and bssid 1988c2ecf20Sopenharmony_ci * fields are equal send it to mgmt frames handler 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (!(memcmp(srcadd, bssid, 6))) { 2018c2ecf20Sopenharmony_ci ret = mon_mgmt_tx(mon_priv->real_ndev, skb->data, skb->len); 2028c2ecf20Sopenharmony_ci if (ret) 2038c2ecf20Sopenharmony_ci netdev_err(dev, "fail to mgmt tx\n"); 2048c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci ret = wilc_mac_xmit(skb, mon_priv->real_ndev); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic const struct net_device_ops wilc_wfi_netdev_ops = { 2138c2ecf20Sopenharmony_ci .ndo_start_xmit = wilc_wfi_mon_xmit, 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistruct net_device *wilc_wfi_init_mon_interface(struct wilc *wl, 2188c2ecf20Sopenharmony_ci const char *name, 2198c2ecf20Sopenharmony_ci struct net_device *real_dev) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct wilc_wfi_mon_priv *priv; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* If monitor interface is already initialized, return it */ 2248c2ecf20Sopenharmony_ci if (wl->monitor_dev) 2258c2ecf20Sopenharmony_ci return wl->monitor_dev; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci wl->monitor_dev = alloc_etherdev(sizeof(struct wilc_wfi_mon_priv)); 2288c2ecf20Sopenharmony_ci if (!wl->monitor_dev) 2298c2ecf20Sopenharmony_ci return NULL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci wl->monitor_dev->type = ARPHRD_IEEE80211_RADIOTAP; 2328c2ecf20Sopenharmony_ci strlcpy(wl->monitor_dev->name, name, IFNAMSIZ); 2338c2ecf20Sopenharmony_ci wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops; 2348c2ecf20Sopenharmony_ci wl->monitor_dev->needs_free_netdev = true; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (register_netdevice(wl->monitor_dev)) { 2378c2ecf20Sopenharmony_ci netdev_err(real_dev, "register_netdevice failed\n"); 2388c2ecf20Sopenharmony_ci free_netdev(wl->monitor_dev); 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci priv = netdev_priv(wl->monitor_dev); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci priv->real_ndev = real_dev; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return wl->monitor_dev; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_civoid wilc_wfi_deinit_mon_interface(struct wilc *wl, bool rtnl_locked) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci if (!wl->monitor_dev) 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (rtnl_locked) 2548c2ecf20Sopenharmony_ci unregister_netdevice(wl->monitor_dev); 2558c2ecf20Sopenharmony_ci else 2568c2ecf20Sopenharmony_ci unregister_netdev(wl->monitor_dev); 2578c2ecf20Sopenharmony_ci wl->monitor_dev = NULL; 2588c2ecf20Sopenharmony_ci} 259