driver_cmd_nl80211.c revision d261d2a4e8c8ba5d52a3f6d24a15404e5a3a49e6
1/*
2 * Driver interaction with extended Linux CFG8021
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Alternatively, this software may be distributed under the terms of BSD
9 * license.
10 *
11 */
12
13#include "driver_nl80211.h"
14
15#define WPA_EVENT_DRIVER_STATE	"CTRL-EVENT-DRIVER-STATE "
16
17#define WPA_PS_ENABLED		0
18#define WPA_PS_DISABLED		1
19
20typedef struct android_wifi_priv_cmd {
21	char *buf;
22	int used_len;
23	int total_len;
24} android_wifi_priv_cmd;
25
26int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
27		       int (*valid_handler)(struct nl_msg *, void *),
28		       void *valid_data);
29
30static int wpa_driver_set_btcoex_state(char state)
31{
32	int ret;
33	int fd;
34
35	fd = open("/sys/devices/platform/bcmdhd/bt_coex_state", O_RDWR, 0);
36	if (fd == -1)
37		return -1;
38
39	ret = write(fd, &state, sizeof(state));
40	close(fd);
41
42	wpa_printf(MSG_DEBUG, "%s: set btcoex state to '%c' result = %d",
43		__func__, state, ret);
44	return (ret > 0) ? 0 : -1;
45}
46
47static int wpa_driver_set_power_save(void *priv, int state)
48{
49	struct i802_bss *bss = priv;
50	struct wpa_driver_nl80211_data *drv = bss->drv;
51	struct nl_msg *msg;
52	int ret = -1;
53	enum nl80211_ps_state ps_state;
54
55	msg = nlmsg_alloc();
56	if (!msg)
57		return -1;
58
59	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
60		    NL80211_CMD_SET_POWER_SAVE, 0);
61
62	if (state == WPA_PS_ENABLED)
63		ps_state = NL80211_PS_ENABLED;
64	else
65		ps_state = NL80211_PS_DISABLED;
66
67	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
68	NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
69
70	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
71	msg = NULL;
72	if (ret < 0)
73		wpa_printf(MSG_ERROR, "nl80211: Set power mode fail: %d", ret);
74nla_put_failure:
75	nlmsg_free(msg);
76	return ret;
77}
78
79static int get_power_mode_handler(struct nl_msg *msg, void *arg)
80{
81	struct nlattr *tb[NL80211_ATTR_MAX + 1];
82	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
83	int *state = (int *)arg;
84
85	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
86		  genlmsg_attrlen(gnlh, 0), NULL);
87
88	if (!tb[NL80211_ATTR_PS_STATE])
89		return NL_SKIP;
90
91	if (state) {
92		*state = (int)nla_get_u32(tb[NL80211_ATTR_PS_STATE]);
93		wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", *state);
94		*state = (*state == NL80211_PS_ENABLED) ?
95				WPA_PS_ENABLED : WPA_PS_DISABLED;
96	}
97
98	return NL_SKIP;
99}
100
101static int wpa_driver_get_power_save(void *priv, int *state)
102{
103	struct i802_bss *bss = priv;
104	struct wpa_driver_nl80211_data *drv = bss->drv;
105	struct nl_msg *msg;
106	int ret = -1;
107	enum nl80211_ps_state ps_state;
108
109	msg = nlmsg_alloc();
110	if (!msg)
111		return -1;
112
113	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
114		    NL80211_CMD_GET_POWER_SAVE, 0);
115
116	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
117
118	ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state);
119	msg = NULL;
120	if (ret < 0)
121		wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret);
122nla_put_failure:
123	nlmsg_free(msg);
124	return ret;
125}
126
127int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
128				  size_t buf_len )
129{
130	struct i802_bss *bss = priv;
131	struct wpa_driver_nl80211_data *drv = bss->drv;
132	struct ifreq ifr;
133	android_wifi_priv_cmd priv_cmd;
134	int ret = 0;
135
136	wpa_msg(drv->ctx, MSG_INFO, "%s: %s", __func__, cmd);
137
138	if (os_strcasecmp(cmd, "STOP") == 0) {
139		linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
140		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
141	} else if (os_strcasecmp(cmd, "START") == 0) {
142		linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1);
143		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
144	} else if (os_strcasecmp(cmd, "MACADDR") == 0) {
145		u8 macaddr[ETH_ALEN] = {};
146
147		ret = linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, macaddr);
148		if (!ret)
149			ret = os_snprintf(buf, buf_len,
150					  "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
151	} else if (os_strcasecmp(cmd, "RELOAD") == 0) {
152		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
153	} else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
154		int state;
155
156		state = atoi(cmd + 10);
157		ret = wpa_driver_set_power_save(priv, state);
158	} else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
159		int state = -1;
160
161		ret = wpa_driver_get_power_save(priv, &state);
162		if (!ret && (state != -1))
163			ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n", state);
164	} else if (os_strncasecmp(cmd, "BTCOEXMODE ", 11) == 0) {
165		char state = cmd[11];
166
167		ret = wpa_driver_set_btcoex_state(state);
168	} else { /* Use private command */
169		memset(&ifr, 0, sizeof(ifr));
170		memset(&priv_cmd, 0, sizeof(priv_cmd));
171		os_memcpy(buf, cmd, strlen(cmd) + 1);
172		os_strncpy(&ifr.ifr_name, bss->ifname, IFNAMSIZ);
173
174		priv_cmd.buf = buf;
175		priv_cmd.used_len = buf_len;
176		priv_cmd.total_len = buf_len;
177		ifr.ifr_data = &priv_cmd;
178
179		if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0)
180			wpa_printf(MSG_ERROR, "%s: failed to issue private commands\n", __func__);
181		else {
182			ret = 0;
183			if ((os_strcasecmp(cmd, "LINKSPEED") == 0) ||
184			    (os_strcasecmp(cmd, "RSSI") == 0))
185				ret = strlen(buf);
186
187			wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
188		}
189	}
190
191	return ret;
192}
193