| /* |
| * Linux cfg80211 Vendor Extension Code |
| * |
| * Copyright (C) 1999-2016, Broadcom Corporation |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2 (the "GPL"), |
| * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
| * following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give you |
| * permission to link this software with independent modules, and to copy and |
| * distribute the resulting executable under terms of your choice, provided that |
| * you also meet, for each linked independent module, the terms and conditions of |
| * the license of that module. An independent module is a module which is not |
| * derived from this software. The special exception does not apply to any |
| * modifications of the software. |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a license |
| * other than the GPL, without Broadcom's express prior written consent. |
| * |
| * $Id: wl_cfgvendor.c 455257 2014-02-13 08:10:24Z $ |
| */ |
| |
| /* |
| * New vendor interface additon to nl80211/cfg80211 to allow vendors |
| * to implement proprietary features over the cfg80211 stack. |
| */ |
| |
| #include <typedefs.h> |
| #include <linuxver.h> |
| #include <osl.h> |
| #include <linux/kernel.h> |
| |
| #include <bcmutils.h> |
| #include <bcmwifi_channels.h> |
| #include <bcmendian.h> |
| #include <proto/ethernet.h> |
| #include <proto/802.11.h> |
| #include <linux/if_arp.h> |
| #include <asm/uaccess.h> |
| |
| #include <dngl_stats.h> |
| #include <dhd.h> |
| #include <dhdioctl.h> |
| #include <wlioctl.h> |
| #include <dhd_cfg80211.h> |
| #ifdef PNO_SUPPORT |
| #include <dhd_pno.h> |
| #endif /* PNO_SUPPORT */ |
| |
| #include <proto/ethernet.h> |
| #include <linux/kernel.h> |
| #include <linux/kthread.h> |
| #include <linux/netdevice.h> |
| #include <linux/sched.h> |
| #include <linux/etherdevice.h> |
| #include <linux/wireless.h> |
| #include <linux/ieee80211.h> |
| #include <linux/wait.h> |
| #include <net/cfg80211.h> |
| #include <net/rtnetlink.h> |
| |
| #include <wlioctl.h> |
| #include <wldev_common.h> |
| #include <wl_cfg80211.h> |
| #include <wl_cfgp2p.h> |
| #include <wl_android.h> |
| #include <wl_cfgvendor.h> |
| #ifdef PROP_TXSTATUS |
| #include <dhd_wlfc.h> |
| #endif |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT) |
| /* |
| * This API is to be used for asynchronous vendor events. This |
| * shouldn't be used in response to a vendor command from its |
| * do_it handler context (instead wl_cfgvendor_send_cmd_reply should |
| * be used). |
| */ |
| int wl_cfgvendor_send_async_event(struct wiphy *wiphy, |
| struct net_device *dev, int event_id, const void *data, int len) |
| { |
| u16 kflags; |
| struct sk_buff *skb; |
| |
| kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL; |
| |
| /* Alloc the SKB for vendor_event */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id, kflags); |
| #else |
| skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, kflags); |
| #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ |
| if (!skb) { |
| WL_ERR(("skb alloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Push the data to the skb */ |
| nla_put_nohdr(skb, len, data); |
| |
| cfg80211_vendor_event(skb, kflags); |
| |
| return 0; |
| } |
| |
| static int wl_cfgvendor_send_cmd_reply(struct wiphy *wiphy, |
| struct net_device *dev, const void *data, int len) |
| { |
| struct sk_buff *skb; |
| |
| /* Alloc the SKB for vendor_event */ |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); |
| if (unlikely(!skb)) { |
| WL_ERR(("skb alloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Push the data to the skb */ |
| nla_put_nohdr(skb, len, data); |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| } |
| |
| static int wl_cfgvendor_priv_string_handler(struct wiphy *wiphy, |
| struct wireless_dev *wdev, const void *data, int len) |
| { |
| struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); |
| int err = 0; |
| int data_len = 0; |
| |
| WL_INFO(("%s: Enter \n", __func__)); |
| |
| bzero(cfg->ioctl_buf, WLC_IOCTL_MAXLEN); |
| |
| if (strncmp((char *)data, BRCM_VENDOR_SCMD_CAPA, strlen(BRCM_VENDOR_SCMD_CAPA)) == 0) { |
| err = wldev_iovar_getbuf(bcmcfg_to_prmry_ndev(cfg), "cap", NULL, 0, |
| cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); |
| if (unlikely(err)) { |
| WL_ERR(("error (%d)\n", err)); |
| return err; |
| } |
| data_len = strlen(cfg->ioctl_buf); |
| cfg->ioctl_buf[data_len] = '\0'; |
| } |
| |
| err = wl_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg), |
| cfg->ioctl_buf, data_len+1); |
| if (unlikely(err)) |
| WL_ERR(("Vendor Command reply failed ret:%d \n", err)); |
| else |
| WL_INFO(("Vendor Command reply sent successfully!\n")); |
| |
| return err; |
| } |
| |
| static const struct wiphy_vendor_command wl_vendor_cmds [] = { |
| { |
| { |
| .vendor_id = OUI_BRCM, |
| .subcmd = BRCM_VENDOR_SCMD_PRIV_STR |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wl_cfgvendor_priv_string_handler |
| }, |
| }; |
| |
| static const struct nl80211_vendor_cmd_info wl_vendor_events [] = { |
| { OUI_BRCM, BRCM_VENDOR_EVENT_UNSPEC }, |
| { OUI_BRCM, BRCM_VENDOR_EVENT_PRIV_STR }, |
| }; |
| |
| int wl_cfgvendor_attach(struct wiphy *wiphy) |
| { |
| |
| WL_INFO(("Vendor: Register BRCM cfg80211 vendor cmd(0x%x) interface \n", |
| NL80211_CMD_VENDOR)); |
| |
| wiphy->vendor_commands = wl_vendor_cmds; |
| wiphy->n_vendor_commands = ARRAY_SIZE(wl_vendor_cmds); |
| wiphy->vendor_events = wl_vendor_events; |
| wiphy->n_vendor_events = ARRAY_SIZE(wl_vendor_events); |
| |
| return 0; |
| } |
| |
| int wl_cfgvendor_detach(struct wiphy *wiphy) |
| { |
| WL_INFO(("Vendor: Unregister BRCM cfg80211 vendor interface \n")); |
| |
| wiphy->vendor_commands = NULL; |
| wiphy->vendor_events = NULL; |
| wiphy->n_vendor_commands = 0; |
| wiphy->n_vendor_events = 0; |
| |
| return 0; |
| } |
| #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT) */ |