blob: f5a51c0573e365af93b3e2af8ccf7dd49a24ca34 [file] [log] [blame]
/*
* Linux cfg80211 vendor command/event handlers of DHD
*
* Copyright 1999-2016, Broadcom Corporation
* All rights reserved,
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided by the copyright holder "as is" and any express or
* implied warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose are disclaimed. In no event
* shall copyright holder be liable for any direct, indirect, incidental, special,
* exemplary, or consequential damages (including, but not limited to, procurement
* of substitute goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether in
* contract, strict liability, or tort (including negligence or otherwise) arising
* in any way out of the use of this software, even if advised of the possibility
* of such damage
*
*
* <<Broadcom-WL-IPTag/Open:>>
*
* $Id: dhd_cfg_vendor.c 525516 2015-01-09 23:12:53Z $
*/
#include <linux/vmalloc.h>
#include <linuxver.h>
#include <net/cfg80211.h>
#include <net/netlink.h>
#include <bcmutils.h>
#ifdef WL_CFG80211_V1
#include <wl_cfg80211_v1.h>
#else
#include <wl_cfg80211.h>
#endif /* WL_CFG80211_V1 */
#include <wl_cfgvendor.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_dbg.h>
#include <dhdioctl.h>
#include <brcm_nl80211.h>
#ifdef VENDOR_EXT_SUPPORT
static int dhd_cfgvendor_priv_string_handler(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int len)
{
const struct bcm_nlmsg_hdr *nlioc = data;
struct net_device *ndev = NULL;
struct bcm_cfg80211 *cfg;
struct sk_buff *reply;
void *buf = NULL, *cur;
dhd_pub_t *dhd;
dhd_ioctl_t ioc = { 0 };
int ret = 0, ret_len, payload, msglen;
int maxmsglen = PAGE_SIZE - 0x100;
int8 index;
WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
DHD_ERROR(("entry: cmd = %d\n", nlioc->cmd));
cfg = wiphy_priv(wiphy);
dhd = cfg->pub;
DHD_OS_WAKE_LOCK(dhd);
/* send to dongle only if we are not waiting for reload already */
if (dhd->hang_was_sent) {
WL_ERR(("HANG was sent up earlier\n"));
DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
DHD_OS_WAKE_UNLOCK(dhd);
return OSL_ERROR(BCME_DONGLE_DOWN);
}
len -= sizeof(struct bcm_nlmsg_hdr);
ret_len = nlioc->len;
if (ret_len > 0 || len > 0) {
if (len > DHD_IOCTL_MAXLEN) {
WL_ERR(("oversize input buffer %d\n", len));
len = DHD_IOCTL_MAXLEN;
}
if (ret_len > DHD_IOCTL_MAXLEN) {
WL_ERR(("oversize return buffer %d\n", ret_len));
ret_len = DHD_IOCTL_MAXLEN;
}
payload = max(ret_len, len) + 1;
buf = vzalloc(payload);
if (!buf) {
DHD_OS_WAKE_UNLOCK(dhd);
return -ENOMEM;
}
memcpy(buf, (void *)nlioc + nlioc->offset, len);
*(char *)(buf + len) = '\0';
}
ndev = wdev_to_wlc_ndev(wdev, cfg);
index = dhd_net2idx(dhd->info, ndev);
if (index == DHD_BAD_IF) {
WL_ERR(("Bad ifidx from wdev:%p\n", wdev));
ret = BCME_ERROR;
goto done;
}
ioc.cmd = nlioc->cmd;
ioc.len = nlioc->len;
ioc.set = nlioc->set;
ioc.driver = nlioc->magic;
ret = dhd_ioctl_process(dhd, index, &ioc, buf);
if (ret) {
WL_TRACE(("dhd_ioctl_process return err %d\n", ret));
ret = OSL_ERROR(ret);
goto done;
}
cur = buf;
while (ret_len > 0) {
msglen = nlioc->len > maxmsglen ? maxmsglen : ret_len;
ret_len -= msglen;
payload = msglen + sizeof(msglen);
reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
if (!reply) {
WL_ERR(("Failed to allocate reply msg\n"));
ret = -ENOMEM;
break;
}
if (nla_put(reply, BCM_NLATTR_DATA, msglen, cur) ||
nla_put_u16(reply, BCM_NLATTR_LEN, msglen)) {
kfree_skb(reply);
ret = -ENOBUFS;
break;
}
ret = cfg80211_vendor_cmd_reply(reply);
if (ret) {
WL_ERR(("testmode reply failed:%d\n", ret));
break;
}
cur += msglen;
}
done:
vfree(buf);
DHD_OS_WAKE_UNLOCK(dhd);
return ret;
}
const struct wiphy_vendor_command dhd_cfgvendor_cmds [] = {
{
{
.vendor_id = OUI_BRCM,
.subcmd = BRCM_VENDOR_SCMD_PRIV_STR
},
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = dhd_cfgvendor_priv_string_handler
},
};
int cfgvendor_attach(struct wiphy *wiphy)
{
wiphy->vendor_commands = dhd_cfgvendor_cmds;
wiphy->n_vendor_commands = ARRAY_SIZE(dhd_cfgvendor_cmds);
return 0;
}
int cfgvendor_detach(struct wiphy *wiphy)
{
wiphy->vendor_commands = NULL;
wiphy->n_vendor_commands = 0;
return 0;
}
#endif /* VENDOR_EXT_SUPPORT */