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