summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c')
-rw-r--r--drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c b/drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c
new file mode 100644
index 000000000000..4ba3827d421a
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd_1363/dhd_cfg_vendor.c
@@ -0,0 +1,174 @@
+/*
+ * Linux cfg80211 vendor command/event handlers of DHD
+ *
+ * Copyright (C) 1999-2017, 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.
+ *
+ *
+ * <<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>
+#include <wl_cfg80211.h>
+#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 */