/* * Copyright (c) 2022, Fuzhou Rockchip Electronics Co., Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include "rk960.h" #include "vendor.h" #include "debug.h" #include <../net/mac80211/ieee80211_i.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static struct wifi_ring_buffer_status ring_buffer[] = { { .name = "rk960_ring_buffer0", .flags = 0, .ring_id = 0, .verbose_level = 0, .written_bytes = 0, .read_bytes = 0, .written_records = 0, }, }; static struct wlan_driver_wake_reason_cnt_t wake_reason_cnt = { .total_cmd_event_wake = 10, }; #endif int rk960_dev_start_mkeep_alive(struct rk960_common *hw_priv, struct rk960_vif *vif, u8 mkeep_alive_id, u8 * ip_pkt, u16 ip_pkt_len, u8 * src_mac, u8 * dst_mac, u32 period_msec) { u8 *data, *pos; data = kzalloc(ip_pkt_len + 14, GFP_KERNEL); if (!data) return -ENOMEM; pos = data; memcpy(pos, dst_mac, 6); pos += 6; memcpy(pos, src_mac, 6); pos += 6; /* Mapping Ethernet type (ETHERTYPE_IP: 0x0800) */ *(pos++) = 0x08; *(pos++) = 0x00; /* Mapping IP pkt */ memcpy(pos, ip_pkt, ip_pkt_len); pos += ip_pkt_len; //add send 802.3 pkt(raw data) kfree(data); return 0; } int rk960_dev_stop_mkeep_alive(struct rk960_common *hw_priv, struct rk960_vif *vif, u8 mkeep_alive_id) { int res = -1; /* * The mkeep_alive packet is for STA interface only; if the bss is configured as AP, * dongle shall reject a mkeep_alive request. */ if (vif->mode != NL80211_IFTYPE_STATION) return res; RK960_DEBUG_STA("%s execution\n", __func__); //add send stop keep alive res = 0; return res; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static int rk960_vendor_start_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { /* max size of IP packet for keep alive */ const int MKEEP_ALIVE_IP_PKT_MAX = 256; int ret = 0, rem, type; u8 mkeep_alive_id = 0; u8 *ip_pkt = NULL; u16 ip_pkt_len = 0; u8 src_mac[6]; u8 dst_mac[6]; u32 period_msec = 0; const struct nlattr *iter; struct ieee80211_local *local = wiphy_priv(wiphy); struct rk960_common *hw_priv = local->hw.priv; struct rk960_vif *vif = rk960_get_vif_from_ieee80211(hw_priv->vif_list[0]); gfp_t kflags = in_atomic()? GFP_ATOMIC : GFP_KERNEL; RK960_DEBUG_STA("%s\n", __func__); nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case MKEEP_ALIVE_ATTRIBUTE_ID: mkeep_alive_id = nla_get_u8(iter); break; case MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN: ip_pkt_len = nla_get_u16(iter); if (ip_pkt_len > MKEEP_ALIVE_IP_PKT_MAX) { ret = -EINVAL; goto exit; } break; case MKEEP_ALIVE_ATTRIBUTE_IP_PKT: if (!ip_pkt_len) { ret = -EINVAL; RK960_ERROR_STA("ip packet length is 0\n"); goto exit; } ip_pkt = (u8 *) kzalloc(ip_pkt_len, kflags); if (ip_pkt == NULL) { ret = -ENOMEM; RK960_ERROR_STA ("Failed to allocate mem for ip packet\n"); goto exit; } memcpy(ip_pkt, (u8 *) nla_data(iter), ip_pkt_len); break; case MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR: memcpy(src_mac, nla_data(iter), 6); break; case MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR: memcpy(dst_mac, nla_data(iter), 6); break; case MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC: period_msec = nla_get_u32(iter); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); ret = -EINVAL; goto exit; } } if (ip_pkt == NULL) { ret = -EINVAL; RK960_ERROR_STA("ip packet is NULL\n"); goto exit; } ret = rk960_dev_start_mkeep_alive(hw_priv, vif, mkeep_alive_id, ip_pkt, ip_pkt_len, src_mac, dst_mac, period_msec); if (ret < 0) { RK960_ERROR_STA("start_mkeep_alive is failed ret: %d\n", ret); } exit: if (ip_pkt) { kfree(ip_pkt); } return ret; } static int rk960_vendor_stop_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type; u8 mkeep_alive_id = 0; const struct nlattr *iter; struct ieee80211_local *local = wiphy_priv(wiphy); struct rk960_common *hw_priv = local->hw.priv; struct rk960_vif *vif = rk960_get_vif_from_ieee80211(hw_priv->vif_list[0]); RK960_DEBUG_STA("%s\n", __func__); nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case MKEEP_ALIVE_ATTRIBUTE_ID: mkeep_alive_id = nla_get_u8(iter); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); ret = -EINVAL; break; } } ret = rk960_dev_stop_mkeep_alive(hw_priv, vif, mkeep_alive_id); if (ret < 0) { RK960_ERROR_STA("stop_mkeep_alive is failed ret: %d\n", ret); } return ret; } static int rk960_vendor_get_ver(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type; const struct nlattr *iter; int payload = 0; char version[128]; int attr = -1; struct sk_buff *reply; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case LOGGER_ATTRIBUTE_DRIVER_VER: memcpy(version, RK960_VERSION, sizeof(RK960_VERSION)); payload = strlen(version); attr = LOGGER_ATTRIBUTE_DRIVER_VER; break; case LOGGER_ATTRIBUTE_FW_VER: memcpy(version, wiphy->fw_version, sizeof(wiphy->fw_version)); payload = strlen(version); attr = LOGGER_ATTRIBUTE_FW_VER; break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } if (attr < 0) return -EINVAL; reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; if (nla_put(reply, attr, payload, version)) { wiphy_err(wiphy, "%s put version error\n", __func__); goto out_put_fail; } ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "%s reply cmd error\n", __func__); return ret; out_put_fail: kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_subcmd_get_channel_list(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type; const struct nlattr *iter; struct sk_buff *reply; int num_channels = 0; int *channel_list = NULL; int payload; int i = 0; struct ieee80211_local *local = wiphy_priv(wiphy); struct rk960_common *hw_priv = local->hw.priv; struct ieee80211_supported_band *band_2GHz = hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ]; num_channels += band_2GHz->n_channels; channel_list = (int *)kzalloc(sizeof(int) * num_channels, GFP_KERNEL); if (!channel_list) return -ENOMEM; for (i = 0; i < band_2GHz->n_channels; i++) channel_list[i] = band_2GHz->channels[i].center_freq; payload = sizeof(num_channels) + sizeof(int) * num_channels + 4; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case GSCAN_ATTRIBUTE_BAND: reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; if (nla_put_u32 (reply, GSCAN_ATTRIBUTE_NUM_CHANNELS, num_channels)) goto out_put_fail; if (nla_put (reply, GSCAN_ATTRIBUTE_CHANNEL_LIST, sizeof(int) * num_channels, channel_list)) goto out_put_fail; ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "%s reply cmd error\n", __func__); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } kfree(channel_list); return ret; out_put_fail: kfree(channel_list); kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_subcmd_set_country_code(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type; const struct nlattr *iter; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case ANDR_WIFI_ATTRIBUTE_COUNTRY: RK960_DEBUG_STA ("%s(%d), ANDR_WIFI_ATTRIBUTE_COUNTRY: %s\n", __func__, __LINE__, (char *)nla_data(iter)); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } /* TODO * Add handle in the future! */ return ret; } static int rk960_vendor_logger_trigger_memory_dump(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { /* TODO * Add handle in the future! */ return 0; } static int rk960_vendor_subcmd_get_feature_set(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret; struct sk_buff *reply; uint32_t feature = 0, payload; payload = sizeof(feature); reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; /* TODO * Add handle in the future! */ /*bit 1:Basic infrastructure mode */ if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) feature |= WIFI_FEATURE_INFRA; /*bit3:HOTSPOT is a supplicant feature, enable it by default */ feature |= WIFI_FEATURE_HOTSPOT; /*bit 4:P2P */ if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) && (wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO))) feature |= WIFI_FEATURE_P2P; /*bit 5:soft AP feature supported */ if (wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) feature |= WIFI_FEATURE_SOFT_AP; /*bit 18:WiFi Logger */ feature |= WIFI_FEATURE_LOGGER; /*bit 21:WiFi mkeep_alive */ feature |= WIFI_FEATURE_MKEEP_ALIVE; if (nla_put_u32(reply, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, feature)) { wiphy_err(wiphy, "%s put u32 error\n", __func__); goto out_put_fail; } ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "%s reply cmd error\n", __func__); return ret; out_put_fail: kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_logger_get_feature(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret; struct sk_buff *reply; uint32_t feature = 0, payload; payload = sizeof(feature); reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; feature |= WIFI_LOGGER_MEMORY_DUMP_SUPPORTED; feature |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED; /*vts will test wake reason state function */ feature |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED; if (nla_put_u32(reply, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, feature)) { wiphy_err(wiphy, "put skb u32 failed\n"); goto out_put_fail; } ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "reply cmd error\n"); return ret; out_put_fail: kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_logger_get_ring_status(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret; struct sk_buff *reply; uint32_t payload; uint32_t ring_buffer_nums = sizeof(ring_buffer) / sizeof(ring_buffer[0]); payload = sizeof(ring_buffer_nums) + sizeof(ring_buffer); reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; if (nla_put_u32(reply, LOGGER_ATTRIBUTE_RING_NUM, ring_buffer_nums)) { wiphy_err(wiphy, "put skb u32 failed\n"); goto out_put_fail; } if (nla_put (reply, LOGGER_ATTRIBUTE_RING_STATUS, sizeof(ring_buffer), ring_buffer)) { wiphy_err(wiphy, "put skb failed\n"); goto out_put_fail; } ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "reply cmd error\n"); return ret; out_put_fail: kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_logger_start_logging(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type, intval, size, i; const struct nlattr *iter; struct wifi_ring_buffer_status rb; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case LOGGER_ATTRIBUTE_LOG_LEVEL: rb.verbose_level = nla_get_u32(iter); break; case LOGGER_ATTRIBUTE_RING_FLAGS: rb.flags = nla_get_u32(iter); break; case LOGGER_ATTRIBUTE_LOG_TIME_INTVAL: intval = nla_get_u32(iter); break; case LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE: size = nla_get_u32(iter); break; case LOGGER_ATTRIBUTE_RING_NAME: strcpy(rb.name, nla_data(iter)); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } ret = -EINVAL; for (i = 0; i < sizeof(ring_buffer) / sizeof(ring_buffer[0]); i++) { if (strcmp(rb.name, ring_buffer[i].name) == 0) { ret = 0; break; } } /* TODO * Add handle in the future */ return ret; } static int rk960_vendor_logger_get_ring_data(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type, i; const struct nlattr *iter; struct wifi_ring_buffer_status rb; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case LOGGER_ATTRIBUTE_RING_NAME: strcpy(rb.name, nla_data(iter)); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } ret = -EINVAL; for (i = 0; i < sizeof(ring_buffer) / sizeof(ring_buffer[0]); i++) { if (strcmp(rb.name, ring_buffer[i].name) == 0) { ret = 0; break; } } /* TODO * Add handle in the future */ return ret; } static int rk960_vendor_logger_get_wake_reason_stats(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret; struct sk_buff *reply; uint32_t payload; payload = sizeof(wake_reason_cnt.total_cmd_event_wake); reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); if (!reply) return -ENOMEM; /* TODO * Add handle in the future */ if (nla_put_u32 (reply, WAKE_STAT_ATTRIBUTE_TOTAL_CMD_EVENT, wake_reason_cnt.total_cmd_event_wake)) goto out_put_fail; ret = cfg80211_vendor_cmd_reply(reply); if (ret) wiphy_err(wiphy, "reply cmd error\n"); return ret; out_put_fail: kfree_skb(reply); return -EMSGSIZE; } static int rk960_vendor_apf_subcmd_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { /* TODO * Add handle in the future */ return 0; } static int rk960_vendor_sub_cmd_set_mac(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int ret = 0, rem, type; const struct nlattr *iter; u8 mac[ETH_ALEN]; nla_for_each_attr(iter, data, len, rem) { type = nla_type(iter); switch (type) { case WIFI_VENDOR_ATTR_DRIVER_MAC_ADDR: memcpy(mac, nla_data(iter), ETH_ALEN); RK960_INFO_STA("%s, %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); break; default: RK960_ERROR_STA("%s(%d), Unknown type: %d\n", __func__, __LINE__, type); return -EINVAL; } } /* TODO * Add handle in the future */ return ret; } #endif static const struct nla_policy rk960_cfg80211_mkeep_alive_policy[MKEEP_ALIVE_ATTRIBUTE_MAX + 1] = { [0] = {.type = NLA_UNSPEC}, [MKEEP_ALIVE_ATTRIBUTE_ID] = {.type = NLA_U8}, [MKEEP_ALIVE_ATTRIBUTE_IP_PKT] = {.type = NLA_MSECS}, [MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN] = {.type = NLA_U16}, [MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR] = {.type = NLA_MSECS, .len = ETH_ALEN}, [MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR] = {.type = NLA_MSECS, .len = ETH_ALEN}, [MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC] = {.type = NLA_U32}, }; static const struct nla_policy rk960_cfg80211_logger_policy[LOGGER_ATTRIBUTE_MAX + 1] = { [0] = {.type = NLA_UNSPEC}, [LOGGER_ATTRIBUTE_DRIVER_VER] = {.type = NLA_BINARY}, [LOGGER_ATTRIBUTE_FW_VER] = {.type = NLA_BINARY}, [LOGGER_ATTRIBUTE_LOG_LEVEL] = {.type = NLA_U32}, [LOGGER_ATTRIBUTE_RING_FLAGS] = {.type = NLA_U32}, [LOGGER_ATTRIBUTE_LOG_TIME_INTVAL] = {.type = NLA_U32}, [LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE] = {.type = NLA_U32}, [LOGGER_ATTRIBUTE_RING_NAME] = {.type = NLA_STRING}, }; static const struct nla_policy rk960_cfg80211_subcmd_policy[GSCAN_ATTRIBUTE_MAX + 1] = { [0] = {.type = NLA_UNSPEC}, [GSCAN_ATTRIBUTE_BAND] = {.type = NLA_U32}, }; static const struct nla_policy rk960_cfg80211_andr_wifi_policy[ANDR_WIFI_ATTRIBUTE_MAX + 1] = { [0] = {.type = NLA_UNSPEC}, [ANDR_WIFI_ATTRIBUTE_COUNTRY] = {.type = NLA_STRING}, }; static const struct nla_policy rk960_cfg80211_subcmd_set_mac_policy[WIFI_VENDOR_ATTR_DRIVER_MAX + 1] = { [0] = {.type = NLA_UNSPEC}, [WIFI_VENDOR_ATTR_DRIVER_MAC_ADDR] = {.type = NLA_MSECS,.len = ETH_ALEN}, }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static int rk960_dump_interface(struct wiphy *wiphy, struct wireless_dev *wdev, struct sk_buff *skb, const void *data, int data_len, unsigned long *storage) { return 0; } const struct wiphy_vendor_command rk960_vendor_cmd[] = { { { .vendor_id = GOOGLE_OUI, .subcmd = WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_start_mkeep_alive, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_mkeep_alive_policy, .maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_stop_mkeep_alive, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_mkeep_alive_policy, .maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_GET_VER}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_get_ver, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_logger_policy, .maxattr = LOGGER_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_subcmd_get_channel_list, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_subcmd_policy, .maxattr = GSCAN_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_subcmd_set_country_code, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_andr_wifi_policy, .maxattr = ANDR_WIFI_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_TRIGGER_MEM_DUMP}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_trigger_memory_dump, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = WIFI_SUBCMD_GET_FEATURE_SET}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_subcmd_get_feature_set, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_GET_FEATURE}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_get_feature, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_GET_RING_STATUS}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_get_ring_status, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_START_LOGGING}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_start_logging, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_logger_policy, .maxattr = LOGGER_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_GET_RING_DATA}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_get_ring_data, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_logger_policy, .maxattr = LOGGER_ATTRIBUTE_MAX #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = LOGGER_GET_WAKE_REASON_STATS}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_logger_get_wake_reason_stats, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = APF_SUBCMD_GET_CAPABILITIES}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = rk960_vendor_apf_subcmd_get_capabilities, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = VENDOR_CMD_RAW_DATA, #endif }, { { .vendor_id = GOOGLE_OUI, .subcmd = VENDOR_NL80211_SUBCMD_SET_MAC}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = rk960_vendor_sub_cmd_set_mac, .dumpit = rk960_dump_interface, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) .policy = rk960_cfg80211_subcmd_set_mac_policy, .maxattr = WIFI_VENDOR_ATTR_DRIVER_MAX, #endif }, }; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) static const struct nl80211_vendor_cmd_info rk960_vendor_events[] = { }; #endif int rk960_vendor_init(struct wiphy *wiphy) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) wiphy->vendor_commands = rk960_vendor_cmd; wiphy->n_vendor_commands = ARRAY_SIZE(rk960_vendor_cmd); wiphy->vendor_events = rk960_vendor_events; wiphy->n_vendor_events = ARRAY_SIZE(rk960_vendor_events); #endif return 0; }