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