+static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
+ unsigned int period, unsigned int interval,
+ unsigned int count, const u8 *device_types,
+ size_t dev_types_len,
+ const u8 *ies, size_t ies_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
+ freq, period, interval, count);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
+ freq) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
+ period) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
+ interval) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
+ count) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
+ dev_types_len, device_types) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
+ ies_len, ies))
+ goto fail;
+
+ nla_nest_end(msg, container);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to send P2P Listen offload vendor command");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_p2p_lo_stop(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_write_to_file(const char *name, unsigned int val)
+{
+ int fd, len;
+ char tmp[128];
+
+ fd = open(name, O_RDWR);
+ if (fd < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to open %s: %s",
+ name, strerror(errno));
+ return fd;
+ }
+
+ len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
+ len = write(fd, tmp, len);
+ if (len < 0)
+ wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
+ name, strerror(errno));
+ close(fd);
+
+ return 0;
+}
+
+
+static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
+{
+ struct i802_bss *bss = priv;
+ char path[128];
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
+ filter_flags);
+
+ /* Configure filtering of unicast frame encrypted using GTK */
+ ret = os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+ bss->ifname);
+ if (os_snprintf_error(sizeof(path), ret))
+ return -1;
+
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_GTK));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set IPv4 unicast in multicast filter");
+ return ret;
+ }
+
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_GTK));
+
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set IPv6 unicast in multicast filter");
+ return ret;
+ }
+
+ /* Configure filtering of unicast frame encrypted using GTK */
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_ARP));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed set gratuitous ARP filter");
+ return ret;
+ }
+
+ /* Configure filtering of IPv6 NA frames */
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_NA));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set unsolicited NA filter");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
+ const u8 **ext_capa, const u8 **ext_capa_mask,
+ unsigned int *ext_capa_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ enum nl80211_iftype nlmode;
+ unsigned int i;
+
+ if (!ext_capa || !ext_capa_mask || !ext_capa_len)
+ return -1;
+
+ nlmode = wpa_driver_nl80211_if_type(type);
+
+ /* By default, use the per-radio values */
+ *ext_capa = drv->extended_capa;
+ *ext_capa_mask = drv->extended_capa_mask;
+ *ext_capa_len = drv->extended_capa_len;
+
+ /* Replace the default value if a per-interface type value exists */
+ for (i = 0; i < drv->num_iface_ext_capa; i++) {
+ if (nlmode == drv->iface_ext_capa[i].iftype) {
+ *ext_capa = drv->iface_ext_capa[i].ext_capa;
+ *ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask;
+ *ext_capa_len = drv->iface_ext_capa[i].ext_capa_len;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+