• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014 Broadcom Corporation
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <linux/vmalloc.h>
18 #include <net/cfg80211.h>
19 #include <net/netlink.h>
20 
21 #include <brcmu_wifi.h>
22 #include "fwil_types.h"
23 #include "dhd.h"
24 #include "p2p.h"
25 #include "dhd_dbg.h"
26 #include "wl_cfg80211.h"
27 #include "vendor.h"
28 #include "fwil.h"
29 
brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int len)30 static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
31 						 struct wireless_dev *wdev,
32 						 const void *data, int len)
33 {
34 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
35 	struct net_device *ndev = cfg_to_ndev(cfg);
36 	const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
37 	struct sk_buff *reply;
38 	int ret, payload, ret_len;
39 	void *dcmd_buf = NULL, *wr_pointer;
40 	u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
41 
42 	brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
43 		  cmdhdr->len);
44 
45 	len -= sizeof(struct brcmf_vndr_dcmd_hdr);
46 	ret_len = cmdhdr->len;
47 	if (ret_len > 0 || len > 0) {
48 		if (len > BRCMF_DCMD_MAXLEN) {
49 			brcmf_err("oversize input buffer %d\n", len);
50 			len = BRCMF_DCMD_MAXLEN;
51 		}
52 		if (ret_len > BRCMF_DCMD_MAXLEN) {
53 			brcmf_err("oversize return buffer %d\n", ret_len);
54 			ret_len = BRCMF_DCMD_MAXLEN;
55 		}
56 		payload = max(ret_len, len) + 1;
57 		dcmd_buf = vzalloc(payload);
58 		if (NULL == dcmd_buf)
59 			return -ENOMEM;
60 
61 		memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len);
62 		*(char *)(dcmd_buf + len)  = '\0';
63 	}
64 
65 	if (cmdhdr->set)
66 		ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
67 					     dcmd_buf, ret_len);
68 	else
69 		ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
70 					     dcmd_buf, ret_len);
71 	if (ret != 0)
72 		goto exit;
73 
74 	wr_pointer = dcmd_buf;
75 	while (ret_len > 0) {
76 		msglen = ret_len > maxmsglen ? maxmsglen : ret_len;
77 		ret_len -= msglen;
78 		payload = msglen + sizeof(msglen);
79 		reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
80 		if (NULL == reply) {
81 			ret = -ENOMEM;
82 			break;
83 		}
84 
85 		if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) ||
86 		    nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) {
87 			kfree_skb(reply);
88 			ret = -ENOBUFS;
89 			break;
90 		}
91 
92 		ret = cfg80211_vendor_cmd_reply(reply);
93 		if (ret)
94 			break;
95 
96 		wr_pointer += msglen;
97 	}
98 
99 exit:
100 	vfree(dcmd_buf);
101 
102 	return ret;
103 }
104 
105 const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
106 	{
107 		{
108 			.vendor_id = BROADCOM_OUI,
109 			.subcmd = BRCMF_VNDR_CMDS_DCMD
110 		},
111 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
112 			 WIPHY_VENDOR_CMD_NEED_NETDEV,
113 		.doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
114 	},
115 };
116