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