index db364f0a471c2d4a881048b535a8406099133948..4a04402c20e5533c1ea02c197bb78bb3691f6708 100644 (file)
#ifdef ANDROID
#include "android_drv.h"
+
+static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+ size_t buf_len);
#endif /* ANDROID */
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+#ifdef ANDROID
+ u8 wowlan_triggers;
+ u8 wowlan_enabled;
+#endif
};
struct wpa_driver_scan_params *params);
static int android_pno_stop(struct i802_bss *bss);
#endif /* ANDROID */
+#ifdef ANDROID_BRCM_P2P_PATCH
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
#ifdef HOSTAPD
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
event.rx_action.data = &mgmt->u.action.category + 1;
event.rx_action.len = frame + len - event.rx_action.data;
wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+#ifdef ANDROID_BRCM_P2P_PATCH
+ } else if (stype == WLAN_FC_STYPE_ASSOC_REQ) {
+ mlme_event_assoc(drv, frame, len);
+ } else if (stype == WLAN_FC_STYPE_DISASSOC) {
+ mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, frame, len);
+ } else if (stype == WLAN_FC_STYPE_DEAUTH) {
+ mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, frame, len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
} else {
event.rx_mgmt.frame = frame;
event.rx_mgmt.frame_len = len;
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI low");
ed.signal_change.above_threshold = 0;
+ } else if (event == NL80211_CQM_RSSI_BEACON_LOSS) {
+ wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+ "event: beacon loss!");
+ wpa_supplicant_event(drv->ctx, EVENT_START_ROAMING, &ed);
} else
return;
}
+static void nl80211_roaming_support_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ int enabled;
+ enum wpa_event_type event;
+
+ enabled = (tb[NL80211_ATTR_ROAMING_DISABLED] == NULL);
+
+ if (enabled)
+ event = EVENT_ROAMING_ENABLED;
+ else
+ event = EVENT_ROAMING_DISABLED;
+
+ wpa_printf(MSG_DEBUG, "nl80211: roaming %s",
+ enabled ? "enabled" : "disabled");
+
+ wpa_supplicant_event(drv->ctx, event, NULL);
+}
+
static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
case NL80211_CMD_NOTIFY_CQM:
nl80211_cqm_event(drv, tb);
break;
+ case NL80211_CMD_ROAMING_SUPPORT:
+ nl80211_roaming_support_event(drv, tb);
+ break;
case NL80211_CMD_REG_CHANGE:
wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+ if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
+ int features = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
+ if (features & NL80211_FEATURE_SCHED_SCAN_INTERVALS)
+ capa->sched_scan_intervals_supported = 1;
+ }
+
if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
capa->max_remain_on_chan =
nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
* wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
* @priv: Pointer to private driver data from wpa_driver_nl80211_init()
* @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
+ * @long_interval: interval between scan cycles after end of short cycles
+ * @short_interval: interval between initial short scan cycles
+ * @num_short_intervals: number of interval short scan intervals
* Returns: 0 on success, -1 on failure or if not supported
*/
static int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params,
- u32 interval)
+ u32 long_interval,
+ u32 short_interval,
+ u8 num_short_intervals)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
- NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+ NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, long_interval);
+
+ if (drv->capa.sched_scan_intervals_supported) {
+ NLA_PUT_U32(msg,
+ NL80211_ATTR_SCHED_SCAN_SHORT_INTERVAL,
+ short_interval);
+
+ NLA_PUT_U8(msg,
+ NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS,
+ num_short_intervals);
+ }
if (drv->num_filter_ssids &&
(int) drv->num_filter_ssids <= drv->capa.max_match_sets) {
goto nla_put_failure;
}
- wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
- "scan interval %d msec", ret, interval);
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) "
+ "intervals: short=%d ms long=%d ms num_short_intervals=%d"
+ , ret, short_interval, long_interval, num_short_intervals);
nla_put_failure:
nlmsg_free(ssids);
1);
}
+#ifdef ANDROID_BRCM_P2P_PATCH
+ if (is_ap_interface(drv->nlmode)) {
+ wpa_printf(MSG_DEBUG, "%s: Sending frame on bss freq %d "
+ "using nl80211_send_frame_cmd", __func__,
+ bss->freq);
+ return nl80211_send_frame_cmd(bss, bss->freq, 0,
+ data, data_len,
+ &drv->send_action_cookie, 0,
+ noack, 0);
+ }
+#else /* ANDROID_BRCM_P2P_PATCH */
if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
if (freq == 0)
freq = bss->freq;
&drv->send_action_cookie,
no_cck, noack, offchanok);
}
+#endif /* ANDROID_BRCM_P2P_PATCH */
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
encrypt = 0;
}
+ wpa_printf(MSG_DEBUG, "%s: Sending frame using monitor interface/"
+ "l2 socket", __func__);
return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
noack, freq, no_cck, offchanok,
wait_time);
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_STATION) {
+ wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+ "allowed in station mode (iftype=%d)",
+ drv->nlmode);
+ return -1;
+ }
+
if (!report) {
if (bss->nl_preq && drv->device_ap_sme &&
is_ap_interface(drv->nlmode)) {
return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+ return -1;
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
static void wpa_driver_nl80211_resume(void *priv)
{
return -ENOBUFS;
}
+#ifdef ANDROID
+
+#define MAX_PATTERN_SIZE 256
+#define MAX_MASK_SIZE (MAX_PATTERN_SIZE/8)
+
+/* Describes a single RX filter configuration */
+struct rx_filter {
+ /* name - A human recongmizable name for the filter */
+ char *name;
+
+ /* get_pattern_handler - A handler which enables the user to configure
+ * the pattern dynamically (For example filter according to the HW addr).
+ * If NULL the static pattern configured will be used.
+ * buf - the pattern will be copied to buf
+ * buflen - the size of buf
+ * arg - A generic input argumet which can be passed to the handler
+ */
+ int (* get_pattern_handler) (u8 *buf, int buflen, void* arg);
+
+ /* pattern - A static pattern to filter
+ * This array contains the bytes of the pattern. The mask field
+ * indicates which bytes should be used in the filter and which
+ * can be discarded
+ */
+ u8 pattern[MAX_PATTERN_SIZE];
+
+ /* pattern_len - The number of bytes used in pattern */
+ u8 pattern_len;
+
+ /* mask - A bit mask indicating which bytes in pattern should be
+ * used for filtering. Each bit here corresponds to a byte in pattern
+ */
+ u8 mask[MAX_MASK_SIZE];
+
+ /* mask_len - The number of bytes used in mask */
+ u8 mask_len;
+};
+
+static u8 *nl80211_rx_filter_get_pattern(struct rx_filter *filter, void *arg)
+{
+ if (filter->get_pattern_handler) {
+ if (filter->get_pattern_handler(filter->pattern,
+ filter->pattern_len, arg)) {
+ return NULL;
+ }
+ }
+
+ return filter->pattern;
+}
+
+static int
+nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
+{
+ int ret;
+ struct i802_bss *bss = (struct i802_bss *)arg;
+
+ ret = linux_get_ifhwaddr(bss->drv->global->ioctl_sock,
+ bss->ifname, buf);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "Failed to get own HW addr (%d)", ret);
+ return -1;
+ }
+ return 0;
+}
+
+static struct rx_filter rx_filters[] = {
+ /* ID 0 */
+ {.name = "self",
+ .pattern = {},
+ .pattern_len = 6,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+ .mask_len = 1,
+ .get_pattern_handler = nl80211_self_filter_get_pattern_handler,
+ },
+
+ /* ID 1 */
+ {.name = "bcast",
+ .pattern = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
+ .pattern_len = 6,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+ .mask_len = 1,
+ },
+
+ /* ID 2 */
+ {.name = "ipv4mc",
+ .pattern = {0x01,0x00,0x5E},
+ .pattern_len = 3,
+ .mask = { BIT(0) | BIT(1) | BIT(2) },
+ .mask_len = 1,
+ },
+
+ /* ID 3 */
+ {.name = "ipv6mc",
+ .pattern = {0x33,0x33},
+ .pattern_len = 2,
+ .mask = { BIT(0) | BIT(1) },
+ .mask_len = 1,
+ },
+
+ /* ID 4 */
+ {.name = "dhcp",
+ .pattern = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0x45, 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0x11,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0x00, 0x44},
+ .pattern_len = 38,
+ .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
+ BIT(6), /* OCTET 2 */
+ BIT(7), /* OCTET 3 */
+ 0, /* OCTET 4 */
+ BIT(4) | BIT(5) }, /* OCTET 5 */
+ .mask_len = 5,
+ },
+
+ /* ID 5 */
+ {.name = "arp",
+ .pattern = {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0x08, 0x06},
+ .pattern_len = 14,
+ .mask = { 0, /* OCTET 1 */
+ BIT(4) | BIT(5) }, /* OCTET 2 */
+ .mask_len = 2,
+ },
+
+ /* ID 6 */
+ {.name = "ssdp",
+ .pattern = {0x01, 0x00, 0x5E, 0 , 0 , 0 , 0 , 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0x45, 0 ,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0x11,
+ 0 , 0 , 0 , 0 , 0 , 0 , 0xEF, 0xFF,
+ 0xFF, 0xFA, 0 , 0 , 0x07, 0x6C},
+ .pattern_len = 38,
+ .mask = { BIT(0) | BIT(1) | BIT(2), /* OCTET 1 */
+ BIT(6), /* OCTET 2 */
+ BIT(7), /* OCTET 3 */
+ BIT(6) | BIT(7), /* OCTET 4 */
+ BIT(0) | BIT(1) | BIT(4) | BIT(5) }, /* OCTET 5 */
+ .mask_len = 5,
+ },
+};
+
+#define NR_RX_FILTERS (int)(sizeof(rx_filters) / sizeof(struct rx_filter))
+
+static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable)
+{
+ struct nl_msg *msg, *pats = NULL;
+ struct nlattr *wowtrig, *pat;
+ int i, ret = -1;
+ int filters;
+
+ bss->drv->wowlan_enabled = !!enable;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, bss->drv->global->nl80211_id, 0,
+ 0, NL80211_CMD_SET_WOWLAN, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->drv->first_bss.ifindex);
+ wowtrig = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+
+ if (!wowtrig) {
+ ret = -ENOBUFS;
+ goto nla_put_failure;
+ }
+
+ if (!enable) {
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+ } else {
+ pats = nlmsg_alloc();
+ if (!pats) {
+ ret = -ENOMEM;
+ goto nla_put_failure;
+ }
+
+ /*
+ * In GB filters 0 and 1 are always set but in ICS they
+ * were completely removed. Add filter 0 (unicast) by default
+ * so unicast traffic won't be dropped in any case.
+ */
+
+ filters = bss->drv->wowlan_triggers |= 1;
+
+ for (i = 0; i < NR_RX_FILTERS; i++) {
+ struct rx_filter *rx_filter = &rx_filters[i];
+ int patnr = 1;
+ u8 *pattern;
+
+ if (!(filters & (1 << i)))
+ continue;
+
+ pattern = nl80211_rx_filter_get_pattern(rx_filter, bss);
+ if (!pattern)
+ continue;
+
+ pat = nla_nest_start(pats, patnr++);
+ NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_MASK,
+ rx_filter->mask_len,
+ rx_filter->mask);
+
+ NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_PATTERN,
+ rx_filter->pattern_len,
+ pattern);
+
+ nla_nest_end(pats, pat);
+ }
+ }
+
+ if (pats)
+ nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, pats);
+
+ nla_nest_end(msg, wowtrig);
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "Failed to set WoWLAN trigger:%d\n", ret);
+
+ if (pats)
+ nlmsg_free(pats);
+
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+static int nl80211_toggle_wowlan_trigger(struct i802_bss *bss, int nr,
+ int enabled)
+{
+ if (nr >= NR_RX_FILTERS) {
+ wpa_printf(MSG_ERROR, "Unknown filter: %d\n", nr);
+ return -1;
+ }
+
+ if (enabled)
+ bss->drv->wowlan_triggers |= 1 << nr;
+ else
+ bss->drv->wowlan_triggers &= ~(1 << nr);
+
+ if (bss->drv->wowlan_enabled)
+ nl80211_set_wowlan_triggers(bss, 1);
+
+ return 0;
+}
+
+static int nl80211_parse_wowlan_trigger_nr(char *s)
+{
+ long i;
+ char *endp;
+
+ i = strtol(s, &endp, 10);
+
+ if(endp == s)
+ return -1;
+ return i;
+}
+
+static int nl80211_toggle_dropbcast(int enable)
+{
+ char filename[90];
+ int rv;
+ FILE *f;
+
+ snprintf(filename, sizeof(filename) - 1,
+ "/sys/bus/platform/devices/wl12xx/drop_bcast");
+ f = fopen(filename, "w");
+ if (!f) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ rv = fprintf(f, "%d", enable);
+ fclose(f);
+ if (rv < 1) {
+ wpa_printf(MSG_DEBUG, "Could not write to file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nl80211_dropbcast_get(char *buf, size_t buf_len)
+{
+ char filename[90], value[10], *pos;
+ int f, rv;
+
+ snprintf(filename, sizeof(filename) - 1,
+ "/sys/bus/platform/devices/wl12xx/drop_bcast");
+ f = open(filename, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ rv = read(f, value, sizeof(value) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ value[rv] = '\0';
+ pos = os_strchr(value, '\n');
+ if (pos)
+ *pos = '\0';
+
+ return snprintf(buf, buf_len, "Drop bcast = %s\n", value);
+}
+
+#endif /* ANDROID */
static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
{
static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
int ctwindow)
{
+#ifdef ANDROID_BRCM_P2P_PATCH
+ char buf[MAX_DRV_CMD_SIZE];
+
+ memset(buf, 0, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps,
+ ctwindow);
+ return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+#else /* ANDROID_BRCM_P2P_PATCH */
struct i802_bss *bss = priv;
wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
return -1; /* Not yet supported */
return nl80211_set_power_save(bss, legacy_ps);
+#endif /* ANDROID_BRCM_P2P_PATCH */
}
if (!ret && (state != -1))
ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n",
state);
- } else { /* Use private command */
- memset(&ifr, 0, sizeof(ifr));
- memset(&priv_cmd, 0, sizeof(priv_cmd));
- os_memcpy(buf, cmd, strlen(cmd) + 1);
- os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
-
- priv_cmd.buf = buf;
- priv_cmd.used_len = buf_len;
- priv_cmd.total_len = buf_len;
- ifr.ifr_data = &priv_cmd;
-
- if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1,
- &ifr)) < 0) {
- wpa_printf(MSG_ERROR, "%s: failed to issue private "
- "commands\n", __func__);
- wpa_driver_send_hang_msg(drv);
+ } else if( os_strncasecmp(cmd, "RXFILTER-ADD ", 13) == 0 ) {
+ int i = nl80211_parse_wowlan_trigger_nr(cmd + 13);
+ if(i < 0)
+ return i;
+ return nl80211_toggle_wowlan_trigger(bss, i, 1);
+ } else if( os_strncasecmp(cmd, "RXFILTER-REMOVE ", 16) == 0 ) {
+ int i = nl80211_parse_wowlan_trigger_nr(cmd + 16);
+ if(i < 0)
+ return i;
+ return nl80211_toggle_wowlan_trigger(bss, i, 0);
+ } else if (os_strcasecmp(cmd, "RXFILTER-START") == 0) {
+ return nl80211_set_wowlan_triggers(bss, 1);
+ } else if (os_strcasecmp(cmd, "RXFILTER-STOP") == 0) {
+ return nl80211_set_wowlan_triggers(bss, 0);
+ } else if (os_strncasecmp(cmd, "DROPBCAST", 9) == 0) {
+ char *value = cmd + 10;
+
+ if (!os_strcasecmp(value, "ENABLE") ||
+ !os_strcasecmp(value, "1")) {
+ ret = nl80211_toggle_dropbcast(1);
+ } else if (!os_strcasecmp(value, "DISABLE") ||
+ !os_strcasecmp(value, "0")) {
+ ret = nl80211_toggle_dropbcast(0);
+ } else if (!os_strcasecmp(value, "GET") ||
+ !os_strlen(value)) {
+ ret = nl80211_dropbcast_get(buf, buf_len);
} else {
- drv_errors = 0;
- ret = 0;
- if ((os_strcasecmp(cmd, "LINKSPEED") == 0) ||
- (os_strcasecmp(cmd, "RSSI") == 0) ||
- (os_strcasecmp(cmd, "GETBAND") == 0))
- ret = strlen(buf);
+ wpa_printf(MSG_ERROR,
+ "Invalid parameter for DROPBCAST: %s",
+ value);
+ ret = -1;
+ }
+ } else if( os_strcasecmp(cmd, "LINKSPEED") == 0 ) {
+ struct wpa_signal_info sig;
+ int linkspeed;
+
+ if (!drv->associated)
+ return -1;
+
+ ret = nl80211_get_link_signal(drv, &sig);
+ if (ret == 0) {
+ linkspeed = sig.current_txrate;
+ ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n",
+ linkspeed);
+ }
+ } else if (os_strcasecmp(cmd, "RSSI") == 0 ||
+ os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
+ struct wpa_signal_info sig;
+ int rssi;
+
+ if (!drv->associated)
+ return -1;
- wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__,
- buf, ret, strlen(buf));
+ ret = nl80211_get_link_signal(drv, &sig);
+ if (ret == 0) {
+ rssi = sig.current_signal;
+ ret = os_snprintf(buf, buf_len, "%s rssi %d\n",
+ drv->ssid, rssi);
}
+ } else {
+ wpa_printf(MSG_ERROR, "Unsupported command: %s", cmd);
+ ret = -1;
}
return ret;
}
+#ifdef ANDROID_BRCM_P2P_PATCH
+
+static int wpa_driver_set_p2p_noa(void *priv, u8 count, int start,
+ int duration)
+{
+ char buf[MAX_DRV_CMD_SIZE];
+
+ memset(buf, 0, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start,
+ duration);
+ return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+}
+
+
+static int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+ char rbuf[MAX_DRV_CMD_SIZE];
+ char *cmd = "P2P_GET_NOA";
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ os_memset(buf, 0, len);
+ ret = wpa_driver_nl80211_driver_cmd(priv, cmd, rbuf, sizeof(rbuf));
+ if (ret <= 0)
+ return 0;
+ ret >>= 1;
+ if (ret > (int)len)
+ ret = (int)len;
+ hexstr2bin(rbuf, buf, ret);
+ return ret;
+}
+
+
+static int wpa_driver_set_ap_wps_p2p_ie(void *priv,
+ const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
+{
+ char buf[MAX_WPSP2PIE_CMD_SIZE];
+ struct wpabuf *ap_wps_p2p_ie = NULL;
+ char *_cmd = "SET_AP_WPS_P2P_IE";
+ char *pbuf;
+ int ret = 0;
+ int i;
+ struct cmd_desc {
+ int cmd;
+ const struct wpabuf *src;
+ } cmd_arr[] = {
+ {0x1, beacon},
+ {0x2, proberesp},
+ {0x4, assocresp},
+ {-1, NULL}
+ };
+
+ wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+ for (i = 0; cmd_arr[i].cmd != -1; i++) {
+ os_memset(buf, 0, sizeof(buf));
+ pbuf = buf;
+ pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
+ *pbuf++ = '\0';
+ ap_wps_p2p_ie = cmd_arr[i].src ?
+ wpabuf_dup(cmd_arr[i].src) : NULL;
+ if (ap_wps_p2p_ie) {
+ os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie),
+ wpabuf_len(ap_wps_p2p_ie));
+ ret = wpa_driver_nl80211_driver_cmd(
+ priv, buf, buf,
+ os_strlen(_cmd) + 3 +
+ wpabuf_len(ap_wps_p2p_ie));
+ wpabuf_free(ap_wps_p2p_ie);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#endif /* ANDROID_BRCM_P2P_PATCH */
+
#endif /* ANDROID */
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = wpa_driver_nl80211_probe_req_report,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
+ .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
.resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action,
.signal_monitor = nl80211_signal_monitor,
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
#endif /* CONFIG_TDLS */
+#ifdef ANDROID_BRCM_P2P_PATCH
+ .get_noa = wpa_driver_get_p2p_noa,
+ .set_noa = wpa_driver_set_p2p_noa,
+ .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_BRCM_P2P_PATCH */
#ifdef ANDROID
.driver_cmd = wpa_driver_nl80211_driver_cmd,
#endif /* ANDROID */