// SPDX-License-Identifier: GPL-2.0
|
|
/******************************************************************************
|
*
|
* Copyright (C) 2020 SeekWave Technology Co.,Ltd.
|
*
|
* This program is free software; you can redistribute it and/or modify
|
* it under the terms of version 2 of the GNU General Public License as
|
* published by the Free Software Foundation;
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*
|
******************************************************************************/
|
|
#include <linux/string.h>
|
#include <linux/ctype.h>
|
#include <net/iw_handler.h>
|
#include <linux/udp.h>
|
#include <linux/if_ether.h>
|
#include <linux/ip.h>
|
#include <net/cfg80211-wext.h>
|
|
#include "skw_core.h"
|
#include "skw_cfg80211.h"
|
#include "skw_iface.h"
|
#include "skw_iw.h"
|
#include "skw_log.h"
|
|
static int skw_iw_commit(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static int skw_iw_get_name(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static int skw_iw_set_freq(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static int skw_iw_get_freq(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static int skw_iw_set_mode(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static int skw_iw_get_mode(struct net_device *dev, struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
|
return 0;
|
}
|
|
static struct iw_statistics *skw_get_wireless_stats(struct net_device *dev)
|
{
|
skw_dbg("traced\n");
|
|
return NULL;
|
}
|
|
static const iw_handler skw_iw_standard_handlers[] = {
|
IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)skw_iw_commit),
|
IW_HANDLER(SIOCGIWNAME, (iw_handler)skw_iw_get_name),
|
IW_HANDLER(SIOCSIWFREQ, (iw_handler)skw_iw_set_freq),
|
IW_HANDLER(SIOCGIWFREQ, (iw_handler)skw_iw_get_freq),
|
IW_HANDLER(SIOCSIWMODE, (iw_handler)skw_iw_set_mode),
|
IW_HANDLER(SIOCGIWMODE, (iw_handler)skw_iw_get_mode),
|
#ifdef CONFIG_CFG80211_WEXT_EXPORT
|
IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange),
|
IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan),
|
IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan),
|
#endif
|
};
|
|
#ifdef CONFIG_WEXT_PRIV
|
|
#define SKW_SET_LEN_64 64
|
#define SKW_SET_LEN_128 128
|
#define SKW_SET_LEN_256 256
|
#define SKW_SET_LEN_512 512
|
#define SKW_GET_LEN_512 512
|
#define SKW_SET_LEN_1024 1024
|
#define SKW_GET_LEN_1024 1024
|
#define SKW_KEEP_BUF_SIZE 1024
|
#define SKW_IW_WOW_BUF_SIZE 1024
|
|
/* max to 16 commands */
|
#define SKW_IW_PRIV_SET (SIOCIWFIRSTPRIV + 1)
|
#define SKW_IW_PRIV_GET (SIOCIWFIRSTPRIV + 3)
|
#define SKW_IW_PRIV_AT (SIOCIWFIRSTPRIV + 5)
|
#define SKW_IW_PRIV_80211MODE (SIOCIWFIRSTPRIV + 6)
|
#define SKW_IW_PRIV_GET_80211MODE (SIOCIWFIRSTPRIV + 7)
|
#define SKW_IW_PRIV_KEEP_ALIVE (SIOCIWFIRSTPRIV + 8)
|
#define SKW_IW_PRIV_WOW_FILTER (SIOCIWFIRSTPRIV + 9)
|
|
#define SKW_IW_PRIV_LAST SIOCIWLASTPRIV
|
|
static struct skw_keep_active_setup kp_set = {0,};
|
static u8 skw_wow_flted[256];
|
|
static int skw_keep_alive_add_checksum(u8 *buff, u32 len)
|
{
|
u8 *ptr = buff;
|
struct iphdr *ip;
|
struct udphdr *udp;
|
__sum16 sum;
|
__wsum sum1;
|
u32 udp_len;
|
|
ptr += sizeof(struct ethhdr);
|
ip = (struct iphdr *)ptr;
|
ip->check = 0;
|
ip->check = cpu_to_le16(ip_compute_csum(ip, 20));
|
|
ptr += sizeof(struct iphdr);
|
udp = (struct udphdr *)ptr;
|
udp->check = 0;
|
|
udp_len = len - sizeof(struct ethhdr)
|
- sizeof(struct iphdr);
|
sum1 = csum_partial(ptr,
|
udp_len, 0);
|
sum = csum_tcpudp_magic(ip->saddr, ip->daddr,
|
udp_len, IPPROTO_UDP, sum1);
|
udp->check = cpu_to_le16(sum);
|
|
skw_dbg("chsum %x %x ip:%x %x sum1:%x udp_len:%d\n", ip->check, sum,
|
ip->saddr, ip->daddr, sum1, udp_len);
|
return 0;
|
}
|
|
static int skw_keep_active_rule_save(struct skw_core *skw,
|
struct skw_keep_active_rule *kp, u8 idx, u8 en, u32 flags)
|
{
|
int ret;
|
|
if (!skw || idx >= SKW_KEEPACTIVE_RULE_MAX) {
|
ret = -EFAULT;
|
return ret;
|
}
|
|
if (kp) {
|
if (kp_set.rule[idx])
|
SKW_KFREE(kp_set.rule[idx]);
|
|
kp_set.rule[idx] = SKW_ZALLOC(kp->payload_len
|
+ sizeof(*kp), GFP_KERNEL);
|
memcpy(kp_set.rule[idx], kp, kp->payload_len + sizeof(*kp));
|
|
if (SKW_KEEPALIVE_ALWAYS_FLAG & flags)
|
kp_set.rule[idx]->always = 1;
|
else
|
kp_set.rule[idx]->always = 0;
|
|
if ((SKW_KEEPALIVE_NEEDCHKSUM_FLAG & flags) &&
|
!(SKW_KEEPALIVE_FWCHKSUM_FLAG & flags)) {
|
skw_keep_alive_add_checksum(kp_set.rule[idx]->data[0].payload,
|
kp_set.rule[idx]->payload_len
|
- sizeof(struct skw_keep_active_rule_data));
|
kp_set.rule[idx]->data[0].is_chksumed = 0;
|
} else if ((SKW_KEEPALIVE_NEEDCHKSUM_FLAG & flags) &&
|
(SKW_KEEPALIVE_FWCHKSUM_FLAG & flags))
|
kp_set.rule[idx]->data[0].is_chksumed = 1;
|
else
|
kp_set.rule[idx]->data[0].is_chksumed = 0;
|
|
kp_set.flags[idx] = flags;
|
}
|
|
if (en)
|
kp_set.en_bitmap |= BIT(idx);
|
else
|
kp_set.en_bitmap &= ~BIT(idx);
|
|
skw_dbg("enable bitmap 0x%x\n", kp_set.en_bitmap);
|
skw_hex_dump("kpsave", &kp_set, sizeof(kp_set), false);
|
|
return 0;
|
}
|
|
static int skw_keep_active_disable_cmd(struct net_device *ndev)
|
{
|
struct skw_spd_action_param spd;
|
int ret = 0;
|
|
spd.sub_cmd = ACTION_DIS_KEEPALIVE;
|
spd.len = 0;
|
|
skw_hex_dump("dpdis:", &spd, sizeof(spd), true);
|
ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev,
|
SKW_CMD_SET_SPD_ACTION, &spd, sizeof(spd), NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
return ret;
|
}
|
|
static int skw_keep_active_append_cmd(struct net_device *ndev,
|
struct skw_core *skw, u32 idx_map, u32 idx)
|
{
|
int ret = 0;
|
u32 rules = 0;
|
int total, fixed, len = 0, offset = 0;
|
struct skw_spd_action_param *spd = NULL;
|
struct skw_keep_active_param *kp_param = NULL;
|
|
fixed = sizeof(struct skw_spd_action_param) +
|
sizeof(struct skw_keep_active_param);
|
total = fixed + SKW_KEEPACTIVE_CMD_BUF_MAX;
|
|
spd = SKW_ZALLOC(total, GFP_KERNEL);
|
if (!spd) {
|
skw_err("malloc failed, size: %d\n", total);
|
return -ENOMEM;
|
}
|
|
kp_param = (struct skw_keep_active_param *)((u8 *)spd
|
+ sizeof(*spd));
|
offset = fixed;
|
|
while (idx_map) {
|
idx = ffs(idx_map) - 1;
|
|
if (!kp_set.rule[idx]) {
|
skw_err("rule exception\n");
|
break;
|
}
|
|
if (offset + sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len > total)
|
break;
|
|
memcpy((u8 *)spd + offset, kp_set.rule[idx],
|
sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len);
|
|
offset += sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len;
|
|
if (++rules > (SKW_KEEPACTIVE_RULE_MAX >> 1))
|
break;
|
|
SKW_CLEAR(idx_map, BIT(idx));
|
}
|
|
kp_param->rule_num = rules;
|
spd->len = offset - sizeof(struct skw_spd_action_param);
|
len = offset;
|
|
spd->sub_cmd = ACTION_EN_KEEPALIVE_APPEND;
|
|
skw_dbg("len:%d rule num:%d\n", len, rules);
|
if (rules) {
|
skw_hex_dump("actvapd:", spd, len, true);
|
ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev,
|
SKW_CMD_SET_SPD_ACTION, spd, len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
SKW_KFREE(spd);
|
return ret;
|
}
|
|
static int skw_keep_active_cmd(struct net_device *ndev, struct skw_core *skw,
|
u8 en, u32 flags)
|
{
|
int ret = 0, append = 0;
|
u32 idx_map, idx, rules = 0;
|
int total, fixed, len = 0, offset = 0;
|
struct skw_spd_action_param *spd = NULL;
|
struct skw_keep_active_param *kp_param = NULL;
|
|
fixed = sizeof(struct skw_spd_action_param) +
|
sizeof(struct skw_keep_active_param);
|
total = fixed + SKW_KEEPACTIVE_CMD_BUF_MAX;
|
|
spd = SKW_ZALLOC(total, GFP_KERNEL);
|
if (!spd) {
|
skw_err("malloc failed, size: %d\n", total);
|
return -ENOMEM;
|
}
|
|
kp_param = (struct skw_keep_active_param *)((u8 *)spd
|
+ sizeof(*spd));
|
offset = fixed;
|
idx_map = kp_set.en_bitmap;
|
|
while (idx_map) {
|
idx = ffs(idx_map) - 1;
|
SKW_CLEAR(idx_map, BIT(idx));
|
|
if (!kp_set.rule[idx]) {
|
skw_err("rule exception\n");
|
break;
|
}
|
|
if (offset + sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len > total)
|
break;
|
|
memcpy((u8 *)spd + offset, kp_set.rule[idx],
|
sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len);
|
|
offset += sizeof(struct skw_keep_active_rule)
|
+ kp_set.rule[idx]->payload_len;
|
|
SKW_CLEAR(idx_map, BIT(idx));
|
|
if (++rules >= (SKW_KEEPACTIVE_RULE_MAX >> 1)) {
|
append++;
|
break;
|
}
|
}
|
|
kp_param->rule_num = rules;
|
spd->len = offset - sizeof(struct skw_spd_action_param);
|
len = offset;
|
|
ret = skw_keep_active_disable_cmd(ndev);
|
|
skw_dbg("len:%d rule num:%d\n", len, rules);
|
if (rules) {
|
spd->sub_cmd = ACTION_EN_KEEPALIVE;
|
skw_hex_dump("actv:", spd, len, true);
|
ret = skw_send_msg(ndev->ieee80211_ptr->wiphy, ndev,
|
SKW_CMD_SET_SPD_ACTION, spd, len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
SKW_KFREE(spd);
|
|
if (append)
|
ret = skw_keep_active_append_cmd(ndev, skw, idx_map, idx);
|
|
return ret;
|
}
|
|
//iwpriv wlan0 keep_alive idx=0,en=1,period=1000,flags=0/1,
|
//pkt=7c:7a:3c:81:e5:72:00:0b
|
static int skw_keep_active_set(struct net_device *dev, u8 *param, int len)
|
{
|
int result_len = 0;
|
u8 *ch, *result_val;
|
char *hex = NULL;
|
char *tmp_hex = NULL;
|
u8 idx, en = 0, get_pkt = 0, send_cnt = 1;
|
u32 flags = 0;
|
u8 keep_alive[SKW_KEEPACTIVE_LENGTH_MAX];
|
struct skw_keep_active_rule *kp =
|
(struct skw_keep_active_rule *)keep_alive;
|
int pos = 0, ret = 0;
|
struct skw_iface *iface = netdev_priv(dev);
|
struct skw_core *skw = iface->skw;
|
|
memset(kp, 0, sizeof(*kp));
|
|
kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_DEF;
|
hex = param;
|
|
hex = strstr(hex, "idx=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("idx param\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("idx param\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
ret = kstrtou8(ch, 0, &idx);
|
if (ret) {
|
skw_err("idx param\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
} else {
|
skw_err("idx not found\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
if (!hex) {
|
ret = -EBADF;
|
goto error;
|
}
|
|
hex = strstr(hex, "en=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("en param\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("en param\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
ret = kstrtou8(ch, 0, &en);
|
if (ret) {
|
skw_err("en param\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
} else {
|
skw_err("en not found\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
if (!hex)
|
goto done;
|
|
hex = strstr(hex, "period=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("period param\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("period param\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
ret = kstrtou32(ch, 0, &kp->keep_interval);
|
if (ret) {
|
skw_err("period param\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
}
|
|
if (!hex)
|
goto done;
|
|
tmp_hex = hex;
|
hex = strstr(tmp_hex, "send_cnt=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("send cnt\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("send cnt\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
ret = kstrtou8(ch, 0, &send_cnt);
|
if (ret) {
|
skw_err("send cnt invalid value\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
|
kp->send_cnt = send_cnt;
|
if (kp->send_cnt <= 0) {
|
kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_DEF;
|
skw_err("send cnt is bigger than %d",
|
SKW_KEEPACTIVE_RULE_SEND_CNT_MAX);
|
}
|
|
if (kp->send_cnt > SKW_KEEPACTIVE_RULE_SEND_CNT_MAX) {
|
kp->send_cnt = SKW_KEEPACTIVE_RULE_SEND_CNT_MAX;
|
skw_err("send cnt is bigger than %d",
|
SKW_KEEPACTIVE_RULE_SEND_CNT_MAX);
|
}
|
} else
|
hex = tmp_hex;
|
|
hex = strstr(hex, "flags=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("flags param\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("flags param\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
ret = kstrtou32(ch, 0, &flags);
|
if (ret) {
|
skw_err("flags param\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
}
|
|
if (!hex)
|
goto done;
|
|
hex = strstr(hex, "pkt=");
|
if (hex) {
|
ch = strsep(&hex, "=");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("pkt param\n");
|
ret = -EFAULT;
|
goto error;
|
}
|
|
result_val = kp->data[0].payload;
|
while (1) {
|
u8 temp = 0;
|
char *cp = strchr(hex, ':');
|
|
if (cp) {
|
*cp = 0;
|
cp++;
|
}
|
|
ret = kstrtou8(hex, 16, &temp);
|
if (ret) {
|
skw_err("pkt param\n");
|
ret = -EINVAL;
|
goto error;
|
}
|
|
if (temp > 255) {
|
skw_err("pkt param\n");
|
ret = -ERANGE;
|
goto error;
|
}
|
|
result_val[pos] = temp;
|
result_len++;
|
pos++;
|
|
if (!cp)
|
break;
|
|
if (result_len + sizeof(*kp) +
|
sizeof(struct skw_keep_active_rule_data) >=
|
SKW_KEEPACTIVE_LENGTH_MAX) {
|
skw_err("len overload\n");
|
ret = -ENOSPC;
|
goto error;
|
}
|
|
hex = cp;
|
}
|
get_pkt = 1;
|
}
|
|
kp->payload_len = result_len + sizeof(struct skw_keep_active_rule_data);
|
|
done:
|
skw_dbg("idx:%d en:%d pr:%d cnt:%d pkt:%d len:%d\n", idx, en,
|
kp->keep_interval, kp->send_cnt, get_pkt, result_len);
|
skw_hex_dump("kp", kp, sizeof(*kp) + kp->payload_len, false);
|
|
if (!(kp->keep_interval && get_pkt))
|
kp = NULL;
|
|
ret = skw_keep_active_rule_save(skw, kp, idx, en, flags);
|
if (ret) {
|
skw_err("save rule\n");
|
goto error;
|
}
|
|
ret = skw_keep_active_cmd(dev, skw, en, flags);
|
if (ret) {
|
skw_err("send rule\n");
|
goto error;
|
}
|
|
return 0;
|
|
error:
|
skw_err("error:%d\n", ret);
|
return ret;
|
}
|
|
//iwpriv wlan0 wow_filter idx=0,pattern=6+7c:7a:3c:81:e5:72#20+!ee:66#50+*3b:27:26:5e:2a
|
//iwpriv wlan0 wow_filter disable
|
//iwpriv wlan0 wow_filter enable/enable_whitelist
|
//iwpriv wlan0 wow_filter "list idx=0"
|
static struct skw_wow_rules_set wow_rules_set = {0,0,0 };
|
static struct skw_wow_user_rules wow_user_rules = {0,};
|
|
static inline int ffs64(u64 x)
|
{
|
int r = 1;
|
|
if (!x)
|
return 0;
|
|
if (!(x & 0xffffffffffffff)) {
|
x >>= 56;
|
r += 56;
|
}
|
|
if (!(x & 0xffffffffffff)) {
|
x >>= 48;
|
r += 48;
|
}
|
|
if (!(x & 0xffffffffff)) {
|
x >>= 40;
|
r += 40;
|
}
|
|
if (!(x & 0xffffffff)) {
|
x >>= 32;
|
r += 32;
|
}
|
|
if (!(x & 0xffffff)) {
|
x >>= 24;
|
r += 24;
|
}
|
|
if (!(x & 0xffff)) {
|
x >>= 16;
|
r += 16;
|
}
|
|
if (!(x & 0xff)) {
|
x >>= 8;
|
r += 8;
|
}
|
|
if (!(x & 0xf)) {
|
x >>= 4;
|
r += 4;
|
}
|
|
if (!(x & 3)) {
|
x >>= 2;
|
r += 2;
|
}
|
|
if (!(x & 1)) {
|
x >>= 1;
|
r += 1;
|
}
|
|
return r;
|
}
|
|
static int skw_send_wow_filter_append_cmd(struct net_device *ndev,
|
struct skw_core *skw, u64 idx_map)
|
{
|
int ret = 0, append = 0;
|
u8 idx, rules = 0;
|
int total, fixed, offset = 0;
|
struct skw_spd_action_param *spd = NULL;
|
struct skw_wow_input_param *wow_param = NULL;
|
struct skw_wow_rule *send_rule = NULL;
|
|
fixed = sizeof(struct skw_spd_action_param) +
|
sizeof(struct skw_wow_input_param);
|
total = fixed + SKW_WOW_RULE_CMD_BUF_MAX; //SKW_MAX_WOW_RULE_NUM_ONE_TIME * struct skw_wow_rule
|
|
spd = SKW_ZALLOC(total, GFP_KERNEL);
|
if (!spd) {
|
skw_err("malloc failed, size: %d\n", total);
|
return -ENOMEM;
|
}
|
|
wow_param = (struct skw_wow_input_param *)((u8 *)spd
|
+ sizeof(*spd));
|
offset = fixed;
|
|
while (idx_map) {
|
idx = ffs64(idx_map) - 1;
|
|
if (offset + sizeof(struct skw_wow_input_param)
|
+ wow_rules_set.rules[idx].len > total)
|
break;
|
|
spd->sub_cmd = ACTION_EN_WOW_APPEND;
|
wow_param->wow_flags = wow_rules_set.wow_flags;
|
memcpy(&wow_param->rules[rules], &wow_rules_set.rules[idx],
|
sizeof(struct skw_wow_rule));
|
|
send_rule = &wow_param->rules[rules];
|
skw_hex_dump("wow_param->rules", &wow_param->rules[rules], sizeof(*send_rule), false);
|
|
SKW_CLEAR(idx_map, (1ULL << idx));
|
|
if (++rules >= SKW_MAX_WOW_RULE_NUM_ONE_TIME) {
|
append++;
|
break;
|
}
|
}
|
wow_param->rule_num = rules;
|
spd->len = sizeof(struct skw_wow_input_param) +
|
sizeof(struct skw_wow_rule) * wow_param->rule_num;
|
|
skw_dbg("wow_param->rule_num:%d len:%d %d\n", wow_param->rule_num, spd->len, total);
|
|
skw_hex_dump("append spd", spd, total, false);
|
|
ret = skw_msg_xmit(ndev->ieee80211_ptr->wiphy, 0,
|
SKW_CMD_SET_SPD_ACTION, spd, total, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
else {
|
memset(skw_wow_flted, 0, sizeof(skw_wow_flted));
|
//memcpy(skw_wow_flted, param, len); //TBD
|
}
|
|
if (append)
|
ret = skw_send_wow_filter_append_cmd(ndev, skw, idx_map);
|
|
return ret;
|
}
|
|
static int skw_send_wow_filter_cmd(struct net_device *ndev, struct skw_core *skw)
|
{
|
int ret = 0, append = 0;
|
u64 idx_map = 0;
|
u8 idx, rules = 0;
|
int total, fixed, offset = 0;
|
struct skw_spd_action_param *spd = NULL;
|
struct skw_wow_input_param *wow_param = NULL;
|
struct skw_wow_rule *send_rule = NULL;
|
|
fixed = sizeof(struct skw_spd_action_param) +
|
sizeof(struct skw_wow_input_param);
|
total = fixed + SKW_WOW_RULE_CMD_BUF_MAX;
|
|
spd = SKW_ZALLOC(total, GFP_KERNEL);
|
if (!spd) {
|
skw_err("malloc failed, size: %d\n", total);
|
return -ENOMEM;
|
}
|
|
wow_param = (struct skw_wow_input_param *)((u8 *)spd
|
+ sizeof(*spd));
|
offset = fixed;
|
|
idx_map = wow_rules_set.en_bitmap;
|
|
while (idx_map) {
|
idx = ffs64(idx_map) - 1;
|
|
if (offset + sizeof(struct skw_wow_input_param)
|
+ wow_rules_set.rules[idx].len > total)
|
break;
|
|
spd->sub_cmd = ACTION_EN_WOW;
|
wow_param->wow_flags = wow_rules_set.wow_flags;
|
memcpy(&wow_param->rules[rules], &wow_rules_set.rules[idx],
|
sizeof(struct skw_wow_rule));
|
|
send_rule = &wow_param->rules[rules];
|
|
skw_hex_dump("wow_param->rules", &wow_param->rules[rules], sizeof(*send_rule), false);
|
SKW_CLEAR(idx_map, (1ULL << idx));
|
|
if (++rules >= SKW_MAX_WOW_RULE_NUM_ONE_TIME) {
|
append++;
|
break;
|
}
|
}
|
wow_param->rule_num = rules;
|
spd->len = sizeof(struct skw_wow_input_param) +
|
sizeof(struct skw_wow_rule) * wow_param->rule_num;
|
|
skw_dbg("wow_param->rule_num:%d len:%d total:%d\n", wow_param->rule_num, spd->len, total);
|
|
skw_hex_dump("spd", spd, sizeof(struct skw_spd_action_param) + spd->len, false);
|
|
ret = skw_msg_xmit(ndev->ieee80211_ptr->wiphy, 0,
|
SKW_CMD_SET_SPD_ACTION, spd, total, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
else {
|
memset(skw_wow_flted, 0, sizeof(skw_wow_flted));
|
//memcpy(skw_wow_flted, param, len); //TBD
|
}
|
|
if (append)
|
ret = skw_send_wow_filter_append_cmd(ndev, skw, idx_map);
|
|
return ret;
|
}
|
|
int skw_wow_filter_set(struct net_device *ndev, u8 *param, int len, char *resp, int *extra_len)
|
{
|
u8 *ch, *result_val, rule_idx = 0;
|
char *hex,*tmp_ch, *ptr;
|
struct skw_wow_rule *rule = NULL;
|
int pos = 0, ret = 0, offset, result_len = 0;
|
struct skw_pkt_pattern *ptn;
|
u8 *data;
|
u32 temp, resp_len = 0;
|
char help[] = "Usage:['list idx=0']|[disable]|[idx=0,pattern=6+10#23+!11][enable/enable_whitelist]";
|
struct skw_iface *iface = netdev_priv(ndev);
|
struct skw_core *skw = iface->skw;
|
|
if (!param)
|
return -EINVAL;
|
|
data = SKW_ZALLOC(SKW_IW_WOW_BUF_SIZE, GFP_KERNEL);
|
if (!data)
|
return -ENOMEM;
|
|
memcpy(data, param, len);
|
hex = data;
|
ptr = hex;
|
|
if (!strncmp(hex, "list idx=", strlen("list idx="))) {
|
if (len <= strlen("list idx=")) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"list cmd");
|
ret = -EFAULT;
|
goto free;
|
}
|
|
hex += strlen("list idx=");
|
|
ret = kstrtou8(hex, 0, &rule_idx);
|
if (ret) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"list cmd idx param");
|
skw_err("idx param\n");
|
ret = -EINVAL;
|
goto free;
|
}
|
|
if (rule_idx >= SKW_MAX_WOW_RULE_NUM) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"list cmd idx param is too big");
|
skw_err("rule_idx param:%d is too big\n", rule_idx);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
if (!((1ULL << rule_idx) & wow_user_rules.en_bitmap)) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"list cmd idx param is unset");
|
skw_err("rule_idx param:%d is unset\n", rule_idx);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
resp_len = sprintf(resp, "List: %s\n", wow_user_rules.rules[rule_idx]);
|
goto free;
|
}
|
|
if (!strcmp(hex, "disable")) {
|
if (len != sizeof("disable")) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"dis cmd");
|
ret = -EFAULT;
|
goto free;
|
}
|
ret = skw_wow_disable(ndev->ieee80211_ptr->wiphy);
|
if (!ret) {
|
memset(skw_wow_flted, 0, sizeof(skw_wow_flted));
|
memcpy(skw_wow_flted, param, len);
|
memset(&wow_rules_set, 0, sizeof(wow_rules_set));
|
memset(&wow_user_rules, 0, sizeof(wow_user_rules));
|
}
|
|
goto free;
|
}
|
|
//Add wow filter by idx
|
if (!strncmp(hex, "idx=", strlen("idx="))) {
|
ch = strsep(&hex, ",");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
skw_err("idx param\n");
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"idx param");
|
ret = -ERANGE;
|
goto free;
|
}
|
|
tmp_ch = strsep((char **)&ch, "=");
|
if ((tmp_ch == NULL) || (strlen(tmp_ch) == 0)) {
|
skw_err("idx param\n");
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"idx param");
|
ret = -EFAULT;
|
goto free;
|
}
|
|
ret = kstrtou8(ch, 0, &rule_idx);
|
if (ret) {
|
skw_err("idx param\n");
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"idx param");
|
ret = -EINVAL;
|
goto free;
|
}
|
|
if (rule_idx >= SKW_MAX_WOW_RULE_NUM) {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"idx param is too big");
|
skw_err("rule_idx param:%d is too big\n", rule_idx);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
//Store the rule
|
wow_user_rules.en_bitmap |= (1ULL << rule_idx);
|
|
strncpy(wow_user_rules.rules[rule_idx], param, len);
|
|
//Translate the wow filter to skw rules
|
rule = &wow_rules_set.rules[rule_idx];
|
result_len = 0;
|
|
ret = strncmp(hex, "pattern=", strlen("pattern="));
|
if (!ret) {
|
hex += strlen("pattern=");
|
result_val = rule->rule;
|
|
while (hex < ptr + len - 1) {
|
ret = sscanf(hex, "%d+%02x",
|
&offset, &temp);
|
if (ret != 2) {
|
ret = sscanf(hex, "%d+!%02x",
|
&offset, &temp);
|
if (ret != 2) {
|
ret = sscanf(hex, "%d+*%02x",
|
&offset, &temp);
|
if (ret != 2) {
|
resp_len = sprintf(resp,
|
"ERROR: %s\n",
|
"match char + +*");
|
ret = -EINVAL;
|
goto free;
|
}
|
}
|
}
|
|
if (offset > ETH_DATA_LEN) {
|
resp_len = sprintf(resp,
|
"ERROR: offset:%d over limit\n",
|
offset);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
ptn = (struct skw_pkt_pattern *)result_val;
|
result_val += sizeof(*ptn);
|
result_len += sizeof(*ptn);
|
|
if (result_len >= sizeof(rule->rule)) {
|
resp_len = sprintf(resp,
|
"ERROR: %s\n",
|
"ptn over limit\n");
|
ret = -ERANGE;
|
goto free;
|
}
|
|
ptn->type_offset = PAT_TYPE_ETH;
|
ptn->offset = offset;
|
|
ch = (u8 *)strsep(&hex, "+");
|
if ((ch == NULL) || (strlen(ch) == 0)) {
|
resp_len = sprintf(resp,
|
"ERROR: %s\n",
|
"match char +\n");
|
ret = -EINVAL;
|
goto free;
|
}
|
|
if (hex[0] == '!') {
|
ptn->op = PAT_OP_TYPE_DIFF;
|
ch = strsep(&hex, "!");
|
} else if (hex[0] == '*') {
|
ptn->op = PAT_OP_TYPE_CONTINUE_MATCH;
|
ch = strsep(&hex, "*");
|
}
|
|
pos = 0;
|
while (hex < ptr + len - 1) {
|
char *cp;
|
|
if (!hex) {
|
ret = -EINVAL;
|
goto free;
|
}
|
|
if (isxdigit(hex[0]) &&
|
isxdigit(hex[1]) &&
|
(sscanf(hex, "%2x", &temp)
|
== 1)) {
|
} else {
|
resp_len = sprintf(resp,
|
"ERROR: match char %c%c end\n",
|
hex[0], hex[1]);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
result_val[pos] = temp;
|
result_len++;
|
pos++;
|
|
if (result_len >= sizeof(rule->rule)) {
|
resp_len = sprintf(resp,
|
"ERROR: %s\n",
|
"size over limit\n");
|
ret = -ERANGE;
|
goto free;
|
}
|
|
if (hex[2] == ',' || hex[2] == '#')
|
break;
|
else if (hex[2] == '\0') {
|
hex += 2;
|
break;
|
} else if (hex[2] != ':') {
|
resp_len = sprintf(resp,
|
"ERROR: char data %c\n",
|
hex[2]);
|
ret = -EINVAL;
|
goto free;
|
}
|
|
cp = strchr(hex, ':');
|
if (cp) {
|
*cp = 0;
|
cp++;
|
}
|
|
hex = cp;
|
}
|
|
result_val += pos;
|
ptn->len = pos;
|
|
if (hex[2] == ',') {
|
hex += 2;
|
break;
|
} else if (hex[2] == '#')
|
ch = strsep(&hex, "#");
|
}
|
} else {
|
resp_len = sprintf(resp, "ERROR: %s\n",
|
"match char pattern=\n");
|
ret = -EINVAL;
|
goto free;
|
}
|
|
rule->len = result_len;
|
wow_rules_set.en_bitmap |= (1ULL << rule_idx);
|
skw_hex_dump("rule", rule, sizeof(*rule), false);
|
ret = 0;
|
|
goto free;
|
}
|
//Make sure enable/enable_whitelist is the final cmd
|
ret = strncmp(hex, "enable", strlen("enable"));
|
if (ret) {
|
resp_len = sprintf(resp, "ERROR\n");
|
ret = -EFAULT;
|
goto free;
|
}
|
|
if (len != sizeof("enable") && len != sizeof("enable_whitelist")) {
|
skw_err("Error: enable");
|
resp_len = sprintf(resp, "Error: enable\n");
|
ret = EINVAL;
|
goto free;
|
}
|
|
hex += strlen("enable");
|
ret = strncmp(hex, "_whitelist", strlen("_whitelist"));
|
if (!ret)
|
hex += strlen("_whitelist");
|
else
|
wow_rules_set.wow_flags |= SKW_WOW_BLACKLIST_FILTER;
|
|
//Send the rules to FW
|
ret = skw_send_wow_filter_cmd(ndev, skw);
|
if (ret) {
|
resp_len = sprintf(resp, "Error: send filter cmd\n");
|
skw_err("Error: send filter cmd");
|
}
|
|
free:
|
|
if (ret)
|
resp_len += sprintf(resp + resp_len, " %s\n", help);
|
else
|
resp_len += sprintf(resp + resp_len, "%s\n", "OK");
|
|
*extra_len = resp_len;
|
|
SKW_KFREE(data);
|
return 0;
|
}
|
|
static int skw_iwpriv_keep_alive(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
char *param = NULL;
|
char help[] = "ERROR useage:[idx=0,en=0/1,period=100,flags=0/1,pkt=7c:11]";
|
int ret = 0;
|
|
WARN_ON(wrqu->data.length > SKW_KEEP_BUF_SIZE);
|
|
param = SKW_ZALLOC(SKW_KEEP_BUF_SIZE, GFP_KERNEL);
|
if (!param) {
|
ret = -ENOMEM;
|
goto out;
|
}
|
|
if (copy_from_user(param, wrqu->data.pointer, SKW_KEEP_BUF_SIZE)) {
|
skw_err("copy failed, length: %d\n",
|
wrqu->data.length);
|
|
ret = -EFAULT;
|
goto free;
|
}
|
|
skw_dbg("cmd: 0x%x, (len: %d)\n",
|
info->cmd, wrqu->data.length);
|
param[SKW_KEEP_BUF_SIZE - 1] = '\0';
|
skw_hex_dump("param:", param, SKW_KEEP_BUF_SIZE, false);
|
|
ret = skw_keep_active_set(dev, param, SKW_KEEP_BUF_SIZE);
|
if (ret)
|
memcpy(extra, help, sizeof(help));
|
else
|
memcpy(extra, "OK", sizeof("OK"));
|
|
wrqu->data.length = SKW_GET_LEN_512;
|
|
skw_dbg("resp: %s\n", extra);
|
|
free:
|
SKW_KFREE(param);
|
|
out:
|
return ret;
|
}
|
|
static int skw_iwpriv_wow_filter(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
char *param;
|
int ret;
|
int extra_len;
|
|
WARN_ON(wrqu->data.length > SKW_IW_WOW_BUF_SIZE);
|
|
param = SKW_ZALLOC(SKW_IW_WOW_BUF_SIZE, GFP_KERNEL);
|
if (!param) {
|
ret = -ENOMEM;
|
goto out;
|
}
|
|
if (copy_from_user(param, wrqu->data.pointer, SKW_IW_WOW_BUF_SIZE)) {
|
skw_err("copy failed, length: %d\n",
|
wrqu->data.length);
|
|
ret = -EFAULT;
|
goto free;
|
}
|
|
skw_hex_dump("flt", param, SKW_IW_WOW_BUF_SIZE, false);
|
|
ret = skw_wow_filter_set(dev, param,
|
min_t(int, SKW_IW_WOW_BUF_SIZE, (int)wrqu->data.length),
|
extra, &extra_len);
|
|
wrqu->data.length = extra_len;
|
|
free:
|
SKW_KFREE(param);
|
|
out:
|
return ret;
|
}
|
|
static int skw_send_at_cmd(struct skw_core *skw, char *cmd, int cmd_len,
|
char *buf, int buf_len)
|
{
|
int ret, len, resp_len, offset;
|
char *command, *resp;
|
|
len = round_up(cmd_len, 4);
|
if (len > SKW_SET_LEN_256)
|
return -E2BIG;
|
|
command = SKW_ZALLOC(SKW_SET_LEN_512, GFP_KERNEL);
|
if (!command) {
|
ret = -ENOMEM;
|
goto out;
|
}
|
|
offset = (long)command & 0x7;
|
if (offset) {
|
offset = 8 - offset;
|
skw_detail("command: 0x%p, offset: %d\n", command, offset);
|
}
|
|
resp_len = round_up(buf_len, skw->hw_pdata->align_value);
|
resp = SKW_ZALLOC(resp_len, GFP_KERNEL);
|
if (!resp) {
|
ret = -ENOMEM;
|
goto fail_alloc_resp;
|
}
|
|
ret = skw_uart_open(skw);
|
if (ret < 0)
|
goto failed;
|
|
memcpy(command + offset, cmd, cmd_len);
|
ret = skw_uart_write(skw, command + offset, len);
|
if (ret < 0)
|
goto failed;
|
|
ret = skw_uart_read(skw, resp, resp_len);
|
if (ret < 0)
|
goto failed;
|
|
memcpy(buf, resp, buf_len);
|
ret = 0;
|
|
failed:
|
SKW_KFREE(resp);
|
|
fail_alloc_resp:
|
SKW_KFREE(command);
|
out:
|
if (ret < 0)
|
skw_err("failed: ret: %d\n", ret);
|
|
return ret;
|
}
|
|
static int skw_iwpriv_mode(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
int i;
|
char param[32] = {0};
|
struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev);
|
|
struct skw_iw_wireless_mode {
|
char *name;
|
enum skw_wireless_mode mode;
|
} modes[] = {
|
{"11B", SKW_WIRELESS_11B},
|
{"11G", SKW_WIRELESS_11G},
|
{"11A", SKW_WIRELESS_11A},
|
{"11N", SKW_WIRELESS_11N},
|
{"11AC", SKW_WIRELESS_11AC},
|
{"11AX", SKW_WIRELESS_11AX},
|
{"11G_ONLY", SKW_WIRELESS_11G_ONLY},
|
{"11N_ONLY", SKW_WIRELESS_11N_ONLY},
|
|
/*keep last*/
|
{NULL, 0}
|
};
|
|
WARN_ON(sizeof(param) < wrqu->data.length);
|
|
if (copy_from_user(param, wrqu->data.pointer, sizeof(param))) {
|
skw_err("copy failed, length: %d\n",
|
wrqu->data.length);
|
|
return -EFAULT;
|
}
|
|
param[31] = '\0';
|
skw_dbg("cmd: 0x%x, %s(len: %d)\n",
|
info->cmd, param, wrqu->data.length);
|
|
for (i = 0; modes[i].name; i++) {
|
if (!strcmp(modes[i].name, (char *)param)) {
|
iface->extend.wireless_mode = modes[i].mode;
|
return 0;
|
}
|
}
|
|
return -EINVAL;
|
}
|
|
static int skw_iwpriv_get_mode(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
skw_dbg("traced\n");
|
return 0;
|
}
|
|
static int skw_iwpriv_help(struct skw_iface *iface, void *param, char *args,
|
char *resp, int resp_len)
|
{
|
int len = 0;
|
struct skw_iwpriv_cmd *cmd = param;
|
|
len = sprintf(resp, " %s:\n", cmd->help_info);
|
cmd++;
|
|
while (cmd->handler) {
|
len += sprintf(resp + len, "%-4.4s %s\n", "", cmd->help_info);
|
cmd++;
|
}
|
|
return 0;
|
}
|
|
static int skw_iwpriv_set_bandcfg(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
u16 res;
|
int ret;
|
|
if (args == NULL)
|
return -EINVAL;
|
|
ret = kstrtou16(args, 10, &res);
|
if (!ret && res < 3) {
|
if (res == 0)
|
iface->extend.scan_band_filter = 0;
|
else if (res == 1)
|
iface->extend.scan_band_filter = BIT(NL80211_BAND_2GHZ);
|
else if (res == 2)
|
iface->extend.scan_band_filter = BIT(NL80211_BAND_5GHZ);
|
|
sprintf(resp, "ok");
|
} else
|
sprintf(resp, "failed");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_get_bandcfg(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
if (!iface->extend.scan_band_filter)
|
sprintf(resp, "bandcfg=%s", "Auto");
|
else if (iface->extend.scan_band_filter & BIT(NL80211_BAND_2GHZ))
|
sprintf(resp, "bandcfg=%s", "2G");
|
else if (iface->extend.scan_band_filter & BIT(NL80211_BAND_5GHZ))
|
sprintf(resp, "bandcfg=%s", "5G");
|
|
return 0;
|
}
|
|
int skw_set_cca_thre_ofdm(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_cca_thre_ofdm *p_ofdm)
|
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_OFDM, p_ofdm, sizeof(struct skw_cca_thre_ofdm))) {
|
skw_err("add cca thre ofdm tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_cca_thre_11b(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_cca_thre_11b *p_cca_11b)
|
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_11B, p_cca_11b, sizeof(struct skw_cca_thre_11b))) {
|
skw_err("add cca thre 11b tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_cca_thre_nowifi(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_cca_thre_nowifi *p_nowifi)
|
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_CCA_THRE_NOWIFI, p_nowifi, sizeof(struct skw_cca_thre_nowifi))) {
|
skw_err("add cca thre nowifi tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
|
int skw_set_edca_params(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_edca_param_s *p_edca_params)
|
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_EDCA_PARAM, p_edca_params, sizeof(struct skw_edca_param_s))) {
|
skw_err("add max ppdu dur tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_max_ppdu_dur(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_max_ppdu_dur *p_mppdu_dur)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("idx: %d, max ppdu dur %d\n", p_mppdu_dur->idx, p_mppdu_dur->max_ppdu_dur);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_MAX_PPDU_DUR, p_mppdu_dur, sizeof(struct skw_max_ppdu_dur))) {
|
skw_err("add max ppdu dur tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_force_rts_rate(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_force_rts_rate *rate)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("2.4G rate: %d, 5G rate %d\n", rate->rts_rate_24G, rate->rts_rate_5G);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve force rts rate tlv failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_FORCE_RTS_RATE, rate, sizeof(struct skw_force_rts_rate))) {
|
skw_err("add force rts rate tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_force_rx_rsp_rate(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_force_rx_rsp_rate *rate)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("11b long rate: %d, 11b short rate %d, ofdm rate %d\n", rate->rx_rsp_rate_11b_long,
|
rate->rx_rsp_rate_11b_short, rate->rx_rsp_rate_ofdm);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve force rx rsp rate tlv failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_FORCE_RX_RSP_RATE, rate, sizeof(struct skw_force_rx_rsp_rate))) {
|
skw_err("add force rx rsp rate tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_scan_time_cmd(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_set_scan_time *time)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("active dwell time: %d, bypass active scan auto time %d\n",
|
time->active_dwell_time, time->bypass_active_acan_auto_time);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve scan time failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_SCAN_TIME, time, sizeof(struct skw_set_scan_time))) {
|
skw_err("add scan time failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_tcp_disconn_wakeup_host(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_set_tcpd_wakeup_host *flag)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("wakeup host:%d\n", flag->enable);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve wakeup host flag failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_TCP_DISCONN_WAKEUP_HOST, flag, sizeof(struct skw_set_tcpd_wakeup_host))) {
|
skw_err("add wakeup host flag failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_rc_min_rate(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_set_rate_control_min_rate *rate)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
skw_dbg("rc min rate:%d\n", rate->rstrict_min_rate);
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve rc min rate failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_MIN_RATE, rate, sizeof(struct skw_set_rate_control_min_rate))) {
|
skw_err("add rc min rate failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_rate_control_rate_change(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_set_rate_control_rate_change *rate)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_RATE_CHANGE_PARAM, rate, sizeof(struct skw_set_rate_control_rate_change))) {
|
skw_err("add failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
int skw_set_rc_spe_rate(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_set_rate_control_special_rate *rate)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc tlv failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve tlv failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, SKW_MIB_SET_RATE_CTRL_SPECIAL_FRM_RATE, rate, sizeof(struct skw_set_rate_control_special_rate))) {
|
skw_err("add tlv failed\n");
|
skw_tlv_free(&conf);
|
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret)
|
skw_err("failed, ret: %d\n", ret);
|
}
|
|
skw_tlv_free(&conf);
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_max_ppdu_dur(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL;
|
struct skw_max_ppdu_dur max_ppdu_dur = {0};
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
p = strchr(args, ',');
|
if (!p) {
|
skw_err("idx not found\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("idx found %s\n", p - 1);
|
|
max_ppdu_dur.idx = simple_strtol(p - 1, NULL, 10);
|
if (max_ppdu_dur.idx < 0 || max_ppdu_dur.idx > 5) {
|
skw_err("idx out of range\n");
|
return -EINVAL;
|
}
|
|
p = p + strlen(",");
|
if (!p) {
|
skw_err("mppdu dur not found\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("mppdu dur found %s\n", p);
|
max_ppdu_dur.max_ppdu_dur = simple_strtol(p, NULL, 10);
|
switch (max_ppdu_dur.idx) {
|
case 0:
|
if (max_ppdu_dur.max_ppdu_dur > 0xFFFF) {
|
max_ppdu_dur.max_ppdu_dur = 0xFFFF;
|
skw_warn("The max ppdu dur of idx 0 is 0xFFFF\n");
|
}
|
|
break;
|
|
case 1:
|
if (max_ppdu_dur.max_ppdu_dur > 0x7FFF) {
|
max_ppdu_dur.max_ppdu_dur = 0x7FFF;
|
skw_warn("The max ppdu dur of idx 1 is 0x7FFF\n");
|
}
|
|
break;
|
|
case 2:
|
case 3:
|
case 4:
|
case 5:
|
if (max_ppdu_dur.max_ppdu_dur > 0x1FFF) {
|
max_ppdu_dur.max_ppdu_dur = 0x1FFF;
|
skw_warn("The max ppdu dur of idx 1 is 0x1FFF\n");
|
}
|
|
break;
|
|
default:
|
break;
|
}
|
|
skw_dbg("max ppdu dur idx %d, dur %d\n", max_ppdu_dur.idx, max_ppdu_dur.max_ppdu_dur);
|
|
ret = skw_set_max_ppdu_dur(iface->wdev.wiphy, iface->ndev, &max_ppdu_dur);
|
if (!ret)
|
sprintf(resp, "set max ppdu dur ok ");
|
else
|
sprintf(resp, "set max ppdu dur failed");
|
|
return ret;
|
}
|
|
static u16 skw_iwpriv_convert_string_to_u (char *ptr, u8 length)
|
{
|
char *buffer = NULL;
|
unsigned long num = 0;
|
int ret = 0;
|
|
buffer = kmalloc(length + 1, GFP_KERNEL);
|
if (!buffer) {
|
skw_err("mem alloc failed\n");
|
return -ENOMEM;
|
}
|
memcpy(buffer, ptr, length);
|
buffer[length] = '\0';
|
|
ret = kstrtoul(buffer, 0, &num);
|
if (ret == 0) {
|
if (num <= 0xFFFF) {
|
kfree(buffer);
|
return (u16)num;
|
} else if (num <= 0xFF) {
|
kfree(buffer);
|
return (u8)num;
|
} else {
|
skw_err("tred beyond u8 and u16\n");
|
kfree(buffer);
|
return -ERANGE;
|
}
|
} else {
|
skw_err("transfer fail\n");
|
kfree(buffer);
|
return -EINVAL;
|
}
|
}
|
|
static u32 skw_iwpriv_convert_string_to_u32(char *ptr, unsigned int length)
|
{
|
if (ptr == NULL || length <= 0) {
|
skw_err("Invalid input parameters");
|
return -EINVAL;
|
}
|
|
if (length >= 2 && ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) {
|
char *buffer = NULL;
|
unsigned long num = 0;
|
int ret = 0;
|
|
buffer = kmalloc(length + 1, GFP_KERNEL);
|
if (!buffer) {
|
skw_err("mem alloc failed\n");
|
return -ENOMEM;
|
}
|
memcpy(buffer, ptr, length);
|
buffer[length] = '\0';
|
|
ret = kstrtoul(buffer, 16, &num);
|
if (ret == 0) {
|
if (num <= 0xFFFFFFFF) {
|
kfree(buffer);
|
return (u32)num;
|
} else {
|
skw_err("value exceeds u32 range");
|
kfree(buffer);
|
return -ERANGE;
|
}
|
} else {
|
skw_err("hex conversion failed");
|
kfree(buffer);
|
return -EINVAL;
|
}
|
} else {
|
skw_err("not a valid hex string");
|
return -EINVAL;
|
}
|
}
|
|
int skw_iwpriv_set_edca_params(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_edca_param_s edca_params = {0};
|
u8 parmas_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_dbg(" not set edca params\n");
|
edca_params.enable = 0;
|
goto SETPARA;
|
}
|
|
skw_err(" need more edca params\n");
|
goto SETFAIL;
|
|
} else if (parmas_len > 1) {
|
p = strchr(args, ',');
|
if (!p) {
|
skw_dbg("flag not found\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("params found %s\n", p - 1);
|
|
edca_params.enable = simple_strtol(p - 1, NULL, 10);
|
if (edca_params.enable != 0 && edca_params.enable != 1) {
|
skw_err("flag error %d\n", edca_params.enable);
|
return -EINVAL;
|
}
|
|
if (edca_params.enable == 0) {
|
skw_err("not set edca params\n");
|
goto SETPARA;
|
}
|
|
skw_dbg("start set edca params ...\n");
|
}
|
|
/* parse AC_BE parametes */
|
/** AciAifn **/
|
//p = p + strlen(",");
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BE AciAifn not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_best_effort.aci_aifn = simple_strtol(p, NULL, 10);
|
edca_params.ac_best_effort.aci_aifn = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_best_effort.aci_aifn > 0xFF) {
|
skw_warn("AC_BE AciAifn limit is 0xFF\n");
|
edca_params.ac_best_effort.aci_aifn = 0xFF;
|
}
|
|
skw_dbg("AC_BE Set AciAifn %d\n", edca_params.ac_best_effort.aci_aifn);
|
|
//return 0;
|
/** EcWminEcWmax **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BE EcWminEcWmax not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_best_effort.ec_wmin_wmax = simple_strtol(p, NULL, 10);
|
edca_params.ac_best_effort.ec_wmin_wmax = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_best_effort.ec_wmin_wmax > 0xFF) {
|
skw_dbg("AC_BE EcWminEcWmax limit is 0xFF\n");
|
edca_params.ac_best_effort.ec_wmin_wmax = 0xFF;
|
}
|
|
skw_dbg("AC_BE Set EcWminEcWmax %d\n", edca_params.ac_best_effort.ec_wmin_wmax);
|
|
/** TxopLimit **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BE TxopLimit not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_best_effort.txop_limit = simple_strtol(p, NULL, 10);
|
edca_params.ac_best_effort.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_best_effort.txop_limit > 0xFFFF) {
|
skw_dbg("AC_BE TxopLimit limit is 0xFF\n");
|
edca_params.ac_best_effort.txop_limit = 0xFFFF;
|
}
|
|
skw_dbg("AC_BE Set TxopLimit %d\n", edca_params.ac_best_effort.txop_limit);
|
|
/* parse AC_BG parametes */
|
/** AciAifn **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BG AciAifn not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_background.aci_aifn = simple_strtol(p, NULL, 10);
|
edca_params.ac_background.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_background.aci_aifn > 0xFF) {
|
skw_warn("AC_BG AciAifn limit is 0xFF\n");
|
edca_params.ac_background.aci_aifn = 0xFF;
|
}
|
|
skw_dbg("AC_BG Set AciAifn %d\n", edca_params.ac_background.aci_aifn);
|
|
/** EcWminEcWmax **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BG EcWminEcWmax not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_background.ec_wmin_wmax = simple_strtol(p, NULL, 10);
|
edca_params.ac_background.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_background.ec_wmin_wmax > 0xFF) {
|
skw_warn("AC_BG EcWminEcWmax limit is 0xFF\n");
|
edca_params.ac_background.ec_wmin_wmax = 0xFF;
|
}
|
|
skw_dbg("AC_BG Set EcWminEcWmax %d\n", edca_params.ac_background.ec_wmin_wmax);
|
|
/** TxopLimit **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_BG TxopLimit not found\n");
|
return -ENOTSUPP;
|
}
|
|
//edca_params.ac_best_effort.txop_limit = simple_strtol(p, NULL, 10);
|
edca_params.ac_background.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_background.txop_limit > 0xFFFF) {
|
skw_warn("AC_BG TxopLimit limit is 0xFF\n");
|
edca_params.ac_background.txop_limit = 0xFFFF;
|
}
|
|
skw_dbg("AC_BG Set TxopLimit %d\n", edca_params.ac_best_effort.txop_limit);
|
|
/* parse AC_VIDEO parametes */
|
/** AciAifn **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_VIDEO AciAifn not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_video.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_video.aci_aifn > 0xFF) {
|
skw_warn("AC_VIDEO AciAifn limit is 0xFF\n");
|
edca_params.ac_video.aci_aifn = 0xFF;
|
}
|
|
skw_dbg("AC_VIDEO Set AciAifn %d\n", edca_params.ac_video.aci_aifn);
|
|
/** EcWminEcWmax **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_VIDEO EcWminEcWmax not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_video.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_video.ec_wmin_wmax > 0xFF) {
|
skw_warn("AC_VIDEO EcWminEcWmax limit is 0xFF\n");
|
edca_params.ac_video.ec_wmin_wmax = 0xFF;
|
}
|
|
skw_dbg("AC_VIDEO Set EcWminEcWmax %d\n", edca_params.ac_video.ec_wmin_wmax);
|
|
/** TxopLimit **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_VIDEO TxopLimit not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_video.txop_limit = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_video.txop_limit > 0xFFFF) {
|
skw_warn("AC_VIDEO TxopLimit limit is 0xFF\n");
|
edca_params.ac_video.txop_limit = 0xFFFF;
|
}
|
|
skw_dbg("AC_VIDEO Set TxopLimit %d\n", edca_params.ac_video.txop_limit);
|
|
/* parse AC_VOICE parametes */
|
/** AciAifn **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_VOICE AciAifn not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_voice.aci_aifn = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_voice.aci_aifn > 0xFF) {
|
skw_warn("AC_VOICE AciAifn limit is 0xFF\n");
|
edca_params.ac_voice.aci_aifn = 0xFF;
|
}
|
|
skw_dbg("AC_VOICE Set AciAifn %d\n", edca_params.ac_voice.aci_aifn);
|
|
/** EcWminEcWmax **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("AC_VOICE EcWminEcWmax not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_voice.ec_wmin_wmax = skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (edca_params.ac_voice.ec_wmin_wmax > 0xFF) {
|
skw_warn("AC_VOICE EcWminEcWmax limit is 0xFF\n");
|
edca_params.ac_voice.ec_wmin_wmax = 0xFF;
|
}
|
|
skw_dbg("AC_VOICE Set EcWminEcWmax %d\n", edca_params.ac_voice.ec_wmin_wmax);
|
|
/** TxopLimit **/
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("AC_VOICE TxopLimit not found\n");
|
return -ENOTSUPP;
|
}
|
|
edca_params.ac_voice.txop_limit = simple_strtol(p + 1, NULL, 10);
|
if (edca_params.ac_voice.txop_limit > 0xFFFF) {
|
skw_warn("AC_VOICE TxopLimit limit is 0xFF\n");
|
edca_params.ac_voice.txop_limit = 0xFFFF;
|
}
|
|
skw_dbg("AC_VOICE Set TxopLimit %d\n", edca_params.ac_voice.txop_limit);
|
|
SETPARA:
|
ret = skw_set_edca_params(iface->wdev.wiphy, iface->ndev, &edca_params);
|
if (!ret)
|
sprintf(resp, "set edca params ok ");
|
else
|
sprintf(resp, "set edca params failed");
|
|
return ret;
|
|
SETFAIL:
|
|
sprintf(resp, "set edca params failed");
|
|
return -EINVAL;
|
}
|
|
static int skw_iwpriv_set_cca_thre_nowifi(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL;
|
struct skw_cca_thre_nowifi nowifi = {0};
|
int temp_val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
p = args;
|
if (!p) {
|
skw_err("nowifi not found\n");
|
return -ENOTSUPP;
|
} else {
|
skw_err("nowifi found %s\n", p);
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre NOWIFI is 0xFF\n");
|
return -EINVAL;
|
}
|
nowifi.val = 0 - temp_val;
|
}
|
|
skw_dbg("nowifi found %s\n", p);
|
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre NOWIFI is 0xFF\n");
|
return -EINVAL;
|
}
|
|
nowifi.val = 0 - temp_val;
|
|
skw_dbg("Set CCA Thre NOWIFI %d\n", nowifi.val);
|
|
ret = skw_set_cca_thre_nowifi(iface->wdev.wiphy, iface->ndev, &nowifi);
|
if (!ret)
|
sprintf(resp, "set CCA Thre NOWIFI ok ");
|
else
|
sprintf(resp, "set CCA Thre NOWIFI failed");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_cca_thre_11b(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL;
|
struct skw_cca_thre_11b cca_11b = {0};
|
int temp_val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
p = args;
|
if (!p) {
|
skw_err("11b not found\n");
|
return -ENOTSUPP;
|
} else {
|
skw_err("11b found %s\n", p);
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre 11b is 0xFF\n");
|
return -EINVAL;
|
}
|
cca_11b.val = 0 - temp_val;
|
}
|
|
skw_dbg("11b found %s\n", p);
|
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre 11b is 0xFF\n");
|
return -EINVAL;
|
}
|
|
cca_11b.val = 0 - temp_val;
|
|
skw_dbg("Set CCA Thre 11b %d\n", cca_11b.val);
|
|
ret = skw_set_cca_thre_11b(iface->wdev.wiphy, iface->ndev, &cca_11b);
|
if (!ret)
|
sprintf(resp, "set CCA Thre 11b ok ");
|
else
|
sprintf(resp, "set CCA Thre 11b failed");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_cca_thre_ofdm(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL;
|
struct skw_cca_thre_ofdm ofdm = {0};
|
int temp_val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
p = args;
|
if (!p) {
|
skw_err("ofdm not found\n");
|
return -ENOTSUPP;
|
} else {
|
skw_err("ofdm found %s\n", p);
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre ofdm is 0xFF\n");
|
return -EINVAL;
|
}
|
ofdm.val = 0 - temp_val;
|
}
|
|
skw_dbg("ofdm found %s\n", p);
|
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("The max CCA Thre ofdm is 0xFF\n");
|
return -EINVAL;
|
}
|
|
ofdm.val = 0 - temp_val;
|
|
skw_dbg("Set CCA Thre OFDM %d\n", ofdm.val);
|
|
ret = skw_set_cca_thre_ofdm(iface->wdev.wiphy, iface->ndev, &ofdm);
|
if (!ret)
|
sprintf(resp, "set CCA Thre ofdm ok ");
|
else
|
sprintf(resp, "set CCA Thre ofdm failed");
|
|
return ret;
|
}
|
|
static int skw_is_value_in_enum(u8 value)
|
{
|
switch (value) {
|
case LEGA_11B_SHORT_2M:
|
case LEGA_11B_SHORT_55M:
|
case LEGA_11B_SHORT_11M:
|
case LEGA_11B_LONG_1M:
|
case LEGA_11B_LONG_2M:
|
case LEGA_11B_LONG_55M:
|
case LEGA_11B_LONG_11M:
|
case OFDM_6M:
|
case OFDM_9M:
|
case OFDM_12M:
|
case OFDM_18M:
|
case OFDM_24M:
|
case OFDM_36M:
|
case OFDM_48M:
|
case OFDM_54M:
|
case HT_MCS_0:
|
case HT_MCS_1:
|
case HT_MCS_2:
|
case HT_MCS_3:
|
case HT_MCS_4:
|
case HT_MCS_5:
|
case HT_MCS_6:
|
case HT_MCS_7:
|
case HT_MCS_8:
|
case HT_MCS_9:
|
case HT_MCS_10:
|
case HT_MCS_11:
|
case HT_MCS_12:
|
case HT_MCS_13:
|
case HT_MCS_14:
|
case HT_MCS_15:
|
case HT_MCS_16:
|
case HT_MCS_17:
|
case HT_MCS_18:
|
case HT_MCS_19:
|
case HT_MCS_20:
|
case HT_MCS_21:
|
case HT_MCS_22:
|
case HT_MCS_23:
|
case HT_MCS_24:
|
case HT_MCS_25:
|
case HT_MCS_26:
|
case HT_MCS_27:
|
case HT_MCS_28:
|
case HT_MCS_29:
|
case HT_MCS_30:
|
case HT_MCS_31:
|
case VHT_MCS_0:
|
case VHT_MCS_1:
|
case VHT_MCS_2:
|
case VHT_MCS_3:
|
case VHT_MCS_4:
|
case VHT_MCS_5:
|
case VHT_MCS_6:
|
case VHT_MCS_7:
|
case VHT_MCS_8:
|
case VHT_MCS_9:
|
case HE_MCS_0:
|
case HE_MCS_1:
|
case HE_MCS_2:
|
case HE_MCS_3:
|
case HE_MCS_4:
|
case HE_MCS_5:
|
case HE_MCS_6:
|
case HE_MCS_7:
|
case HE_MCS_8:
|
case HE_MCS_9:
|
case HE_MCS_10:
|
case HE_MCS_11:
|
case ER_NDCM_1SS_242TONE_MCS0:
|
case ER_NDCM_1SS_242TONE_MCS1:
|
case ER_NDCM_1SS_242TONE_MCS2:
|
case ER_NDCM_1SS_106TONE_MCS0:
|
case ER_DCM_1SS_242TONE_MCS0:
|
case ER_DCM_1SS_242TONE_MCS1:
|
case ER_DCM_1SS_106TONE_MCS0:
|
case NER_DCM_1SS_MCS0:
|
case NER_DCM_1SS_MCS1:
|
case NER_DCM_1SS_MCS3:
|
case NER_DCM_1SS_MCS4:
|
case NER_DCM_2SS_MCS0:
|
case NER_DCM_2SS_MCS1:
|
case NER_DCM_2SS_MCS3:
|
case NER_DCM_2SS_MCS4:
|
return 1;
|
|
default:
|
return 0;
|
}
|
}
|
|
static int skw_iwpriv_set_force_rts_rate(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_force_rts_rate rts_rate = {0};
|
u8 parmas_len = 0, val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
/* parse rts rate flag */
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_warn(" not force set rts rate\n");
|
rts_rate.enable = 0;
|
goto SETPARA;
|
}
|
|
skw_warn(" need more rate params\n");
|
goto SETFAIL;
|
|
} else if (parmas_len > 1) {
|
p = strchr(args, ',');
|
if (!p) {
|
skw_err("flag not found\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("params found %s\n", p - 1);
|
|
rts_rate.enable = simple_strtol(p - 1, NULL, 10);
|
if (rts_rate.enable != 0 && rts_rate.enable != 1) {
|
skw_err("flag error %d\n", rts_rate.enable);
|
return -EINVAL;
|
}
|
|
if (rts_rate.enable == 0) {
|
skw_err("not set rts_rate params\n");
|
goto SETPARA;
|
}
|
|
skw_err("start set rts_rate params ...\n");
|
}
|
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("rts rate 2.4G not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (!skw_is_value_in_enum(val)) {
|
skw_warn("invalid 2.4G rate %d\n", val);
|
return -EINVAL;
|
}
|
|
rts_rate.rts_rate_24G = val;
|
|
skw_dbg("rts rate 2.4G: %d\n", rts_rate.rts_rate_24G);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("rts rate 5G not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (!skw_is_value_in_enum(val)) {
|
skw_warn("invalid 5G rate %d\n", val);
|
return -EINVAL;
|
}
|
|
rts_rate.rts_rate_5G = val;
|
skw_dbg("rts_rate_5G: %d\n", rts_rate.rts_rate_5G);
|
|
SETPARA:
|
ret = skw_set_force_rts_rate(iface->wdev.wiphy, iface->ndev, &rts_rate);
|
if (!ret)
|
sprintf(resp, "set force rts rate ok ");
|
else
|
sprintf(resp, "set force rts rate failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set force rts rate failed");
|
|
return -EINVAL;
|
}
|
|
static int skw_iwpriv_set_force_rx_rsp_rate(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_force_rx_rsp_rate rate = {0};
|
u8 parmas_len = 0, val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("skw_iwpriv_set_force_rts_rate args %s\n", args);
|
|
/* parse rts rate flag */
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_warn(" not force set rx rsp rate\n");
|
goto SETPARA;
|
} else if (ret == 1) {
|
skw_warn(" need more rate params\n");
|
goto SETFAIL;
|
} else {
|
goto SETFAIL;
|
}
|
} else if (parmas_len > 1) {
|
p = strchr(args, ',');
|
if (!p) {
|
skw_err("flag not found\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("params found %s\n", p - 1);
|
|
rate.enable = simple_strtol(p - 1, NULL, 10);
|
if (rate.enable != 0 && rate.enable != 1) {
|
skw_err("flag error %d\n", rate.enable);
|
return -EINVAL;
|
}
|
|
if (rate.enable == 0) {
|
skw_err("not set rx rsp rate params\n");
|
goto SETPARA;
|
}
|
|
skw_dbg("start set rx rsp rate params ...\n");
|
}
|
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("11b long not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (!skw_is_value_in_enum(val)) {
|
skw_warn("invalid 11b long rate %d\n", val);
|
return -EINVAL;
|
}
|
|
rate.rx_rsp_rate_11b_long = val;
|
skw_dbg("11b long rate: %d\n", rate.rx_rsp_rate_11b_long);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("11b short not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (!skw_is_value_in_enum(val)) {
|
skw_warn("invalid 11b short rate %d\n", val);
|
return -EINVAL;
|
}
|
|
rate.rx_rsp_rate_11b_short = val;
|
skw_dbg("11b short rate: %d\n", rate.rx_rsp_rate_11b_short);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("ofdm not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
if (!skw_is_value_in_enum(val)) {
|
skw_warn("invalid ofdm rate %d\n", val);
|
return -EINVAL;
|
}
|
|
rate.rx_rsp_rate_ofdm = val;
|
skw_dbg("ofdm: %d\n", rate.rx_rsp_rate_ofdm);
|
|
SETPARA:
|
ret = skw_set_force_rx_rsp_rate(iface->wdev.wiphy, iface->ndev, &rate);
|
if (!ret)
|
sprintf(resp, "set force rx rsp rate ok ");
|
else
|
sprintf(resp, "set force rx rsp rate failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set force rts rate failed");
|
|
return -EINVAL;
|
}
|
|
static int skw_iwpriv_set_scan_time(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_set_scan_time time = {0};
|
u8 parmas_len = 0;
|
u16 val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args: %s\n", args);
|
|
/* parse rts rate flag */
|
parmas_len = strlen(args);
|
|
p = args;
|
q = strchr(args, ',');
|
if (!q) {
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("params found %s\n", p - 1);
|
val = skw_iwpriv_convert_string_to_u(p, q - p);
|
if (val > 0xFF) {
|
skw_warn("active dwell limit to 0xFF\n");
|
time.active_dwell_time = 0xFF;
|
} else {
|
time.active_dwell_time = val;
|
}
|
|
skw_dbg("active dwell time: %d\n", time.active_dwell_time);
|
|
p = q + strlen(",");
|
if (!p) {
|
skw_warn("bypass active scan auto time flag not found\n");
|
return -ENOTSUPP;
|
}
|
|
val = simple_strtol(p, NULL, 10);
|
if (val != 0 && val != 1) {
|
skw_err("flag error %d\n", val);
|
return -EINVAL;
|
}
|
|
time.bypass_active_acan_auto_time = val;
|
|
skw_dbg("bypass active scan auto time: %d\n", time.bypass_active_acan_auto_time);
|
|
ret = skw_set_scan_time_cmd(iface->wdev.wiphy, iface->ndev, &time);
|
if (!ret)
|
sprintf(resp, "set scan time ok ");
|
else
|
sprintf(resp, "set scan time failed");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_tcpd_wakeup_host(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_set_tcpd_wakeup_host set_flag = {0};
|
u8 parmas_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("set_tcpd_wakeup_host args %s\n", args);
|
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_dbg("flag: %d\n", ret);
|
set_flag.enable = 0;
|
} else if (ret == 1) {
|
skw_dbg("flag: %d\n", ret);
|
set_flag.enable = 1;
|
} else {
|
goto SETFAIL;
|
}
|
} else if (parmas_len > 1) {
|
skw_err("params error\n");
|
goto SETFAIL;
|
}
|
|
skw_dbg("wakeup host: %d\n", set_flag.enable);
|
|
ret = skw_set_tcp_disconn_wakeup_host(iface->wdev.wiphy, iface->ndev, &set_flag);
|
if (!ret)
|
sprintf(resp, "set tcp disc wakeup host ok ");
|
else
|
sprintf(resp, "set tcp disc wakeup host failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set tcp disc wakeup host failed(param error)");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_rc_min_rate(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_set_rate_control_min_rate rate = {0};
|
u8 parmas_len = 0, val = 0;
|
char *p = NULL;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("set_rc_min_rate args %s\n", args);
|
|
parmas_len = strlen(args);
|
if (!parmas_len)
|
return -EINVAL;
|
|
p = args;
|
val = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len);
|
if (!skw_is_value_in_enum(val)) {
|
skw_dbg("val not valid %d\n", val);
|
goto SETFAIL;
|
}
|
|
rate.rstrict_min_rate = val;
|
skw_dbg("rc min rate: %d\n", rate.rstrict_min_rate);
|
|
ret = skw_set_rc_min_rate(iface->wdev.wiphy, iface->ndev, &rate);
|
if (!ret)
|
sprintf(resp, "set rc min rate ok");
|
else
|
sprintf(resp, "set rc min rate failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set rc min rate failed (value not support)");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_rc_rate_change(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_set_rate_control_rate_change rate = {0};
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("set_rc_rate_change args %s\n", args);
|
|
//1/5 up rate class num
|
p = args;
|
q = strchr(args, ',');
|
if (!p || !q) {
|
skw_err("up rate class num not found\n");
|
return -ENOTSUPP;
|
}
|
|
rate.up_rate_class_num = (u8)skw_iwpriv_convert_string_to_u(p, q - p);
|
|
skw_dbg("up_rate_class_num: %d\n", rate.up_rate_class_num);
|
|
//2/5 down rate class num
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("down rate class num not found\n");
|
return -ENOTSUPP;
|
}
|
|
rate.down_rate_class_num = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("down_rate_class_num: %d\n", rate.down_rate_class_num);
|
|
//3/5 hw rty limit
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("hw rty limit not found\n");
|
return -ENOTSUPP;
|
}
|
|
rate.hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
skw_dbg("hw_rty_limit: %d\n", rate.hw_rty_limit);
|
|
//4/5 per rate hw rty limit
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("per rate hw rty limit not found\n");
|
return -ENOTSUPP;
|
}
|
|
rate.per_rate_hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("per rate hw_rty_limit: %d\n", rate.per_rate_hw_rty_limit);
|
|
//5/5 per rate probe hw rty limit
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("per rate probe hw rty limit not found\n");
|
return -ENOTSUPP;
|
}
|
|
rate.per_rate_probe_hw_rty_limit = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("per rate probe hw_rty_limit: %d\n", rate.per_rate_probe_hw_rty_limit);
|
|
ret = skw_set_rate_control_rate_change(iface->wdev.wiphy, iface->ndev, &rate);
|
if (!ret)
|
sprintf(resp, "set force rx rsp rate ok ");
|
else
|
sprintf(resp, "set force rx rsp rate failed");
|
|
return ret;
|
}
|
|
static int skw_iwpriv_set_rc_spe_rate(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_set_rate_control_special_rate rate = {0};
|
u8 parmas_len = 0, val = 0;
|
char *p = NULL;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (!parmas_len)
|
return -EINVAL;
|
|
p = args;
|
val = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len);
|
if (!skw_is_value_in_enum(val)) {
|
skw_dbg("val not valid %d\n", val);
|
goto SETFAIL;
|
}
|
|
rate.special_frm_rate = val;
|
skw_dbg("rc special rate: %d\n", rate.special_frm_rate);
|
|
ret = skw_set_rc_spe_rate(iface->wdev.wiphy, iface->ndev, &rate);
|
if (!ret)
|
sprintf(resp, "set rc special rate ok ");
|
else
|
sprintf(resp, "set rc special rate failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set rc spe rate failed (value not support)");
|
|
return ret;
|
}
|
|
static int skw_generic_tlv_operation(struct wiphy *wiphy, struct net_device *dev,
|
void *param, size_t param_len, enum SKW_MIB_ID tlv_type)
|
{
|
int ret = 0;
|
u16 *plen;
|
struct skw_tlv_conf conf;
|
struct skw_tlv_get_assign_addr_rsp resp = {0};
|
|
ret = skw_tlv_alloc(&conf, 512, GFP_KERNEL);
|
if (ret) {
|
skw_err("alloc tlv failed\n");
|
return ret;
|
}
|
|
plen = skw_tlv_reserve(&conf, 2);
|
if (!plen) {
|
skw_err("reserve tlv failed\n");
|
skw_tlv_free(&conf);
|
return -ENOMEM;
|
}
|
|
if (skw_tlv_add(&conf, tlv_type, param, param_len)) {
|
skw_err("add tlv failed\n");
|
skw_tlv_free(&conf);
|
return -EINVAL;
|
}
|
|
if (conf.total_len) {
|
*plen = conf.total_len;
|
if (tlv_type == SKW_MIB_GET_ASSIGN_ADDR_VAL_E) {
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, &resp, sizeof(resp));
|
|
if (ret) {
|
skw_err("failed, ret: %d\n", ret);
|
goto SETFAIL;
|
}
|
skw_dbg("read done val: 0x%08x\n", resp.val);
|
} else {
|
ret = skw_send_msg(wiphy, dev, SKW_CMD_SET_MIB, conf.buff,
|
conf.total_len, NULL, 0);
|
if (ret) {
|
skw_err("failed, ret: %d\n", ret);
|
goto SETFAIL;
|
}
|
}
|
}
|
SETFAIL:
|
skw_tlv_free(&conf);
|
return ret;
|
}
|
|
static int skw_set_tx_lifetime(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_tx_lifetime *lifetime)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, lifetime,
|
sizeof(struct skw_tlv_set_tx_lifetime),
|
SKW_MIB_SET_TX_LIFETIME);
|
}
|
|
static int skw_iwpriv_set_tx_lifetime(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_tlv_set_tx_lifetime lftm = {0};
|
u8 parmas_len = 0;
|
char *p = NULL;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (!parmas_len)
|
return -EINVAL;
|
|
p = args;
|
lftm.lifetime = skw_iwpriv_convert_string_to_u(p, parmas_len);
|
|
skw_dbg("set tx lifetime: %d\n", lftm.lifetime);
|
|
ret = skw_set_tx_lifetime(iface->wdev.wiphy, iface->ndev, &lftm);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
static int skw_set_tx_retry_cnt(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_retry_cnt *rtycnt)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, rtycnt,
|
sizeof(struct skw_tlv_set_retry_cnt),
|
SKW_MIB_SET_TX_RETRY_CNT);
|
}
|
|
static int skw_iwpriv_set_tx_retry_cnt(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_tlv_set_retry_cnt cnt = {0};
|
u8 parmas_len = 0;
|
char *p = NULL;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (!parmas_len)
|
return -EINVAL;
|
|
p = args;
|
cnt.rtycnt = (u8)skw_iwpriv_convert_string_to_u(p, parmas_len);
|
|
skw_dbg("set tx retry cnt: %d\n", cnt.rtycnt);
|
|
ret = skw_set_tx_retry_cnt(iface->wdev.wiphy, iface->ndev, &cnt);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
static int skw_set_tx_rts_thrd(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_tx_rts_thrd *thrd)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, thrd,
|
sizeof(struct skw_tlv_set_tx_rts_thrd),
|
SKW_MIB_SET_TX_RTS_THRD);
|
}
|
|
static int skw_iwpriv_set_tx_rts_thrd(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_tlv_set_tx_rts_thrd thrd = {0};
|
u8 parmas_len = 0;
|
char *p = NULL;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (!parmas_len)
|
return -EINVAL;
|
|
p = args;
|
thrd.rts_thrd = skw_iwpriv_convert_string_to_u(p, parmas_len);
|
|
skw_dbg("set tx rts thrd: %d\n", thrd.rts_thrd);
|
|
ret = skw_set_tx_rts_thrd(iface->wdev.wiphy, iface->ndev, &thrd);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
static int skw_set_rx_sepcial_80211_frame(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_rx_special_80211_frame *frm)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, frm,
|
sizeof(struct skw_tlv_set_rx_special_80211_frame),
|
SKW_MIB_SET_FORCE_RX_SPECIAL_80211FRAME);
|
}
|
|
static int skw_iwpriv_set_rx_sepcial_80211_frame(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_tlv_set_rx_special_80211_frame frm = {0};
|
u8 parmas_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("set_rx_sepcial_80211_frame args %s\n", args);
|
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_warn("disable rx\n");
|
frm.en = 0;
|
goto SETPARA;
|
} else if (ret == 1) {
|
skw_warn("need more params\n");
|
goto SETFAIL;
|
} else {
|
goto SETFAIL;
|
}
|
} else if (parmas_len > 1) {
|
p = args;
|
q = strchr(args, ',');
|
if (!p || !q) {
|
skw_err("flag not found\n");
|
return -ENOTSUPP;
|
}
|
|
frm.en = (u8)skw_iwpriv_convert_string_to_u(p, q - p);
|
if (frm.en != 0 && frm.en != 1) {
|
skw_err("flag error %d\n", frm.en);
|
return -EINVAL;
|
}
|
|
if (frm.en == 0) {
|
skw_warn("disable rx\n");
|
frm.en = 0;
|
goto SETPARA;
|
}
|
|
skw_err("start set type and sub type params ...\n");
|
}
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !q) {
|
skw_err("param error\n");
|
return -ENOTSUPP;
|
}
|
|
frm.type = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("type: %d\n", frm.type);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("sub type not found\n");
|
return -ENOTSUPP;
|
}
|
|
frm.sub_type = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("sub type: %d\n", frm.sub_type);
|
|
SETPARA:
|
ret = skw_set_rx_sepcial_80211_frame(iface->wdev.wiphy, iface->ndev, &frm);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
|
SETFAIL:
|
|
sprintf(resp, "set failed");
|
|
return -EINVAL;
|
}
|
|
static int skw_set_rx_update_nav(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_rx_update_nav *nav)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, nav,
|
sizeof(struct skw_tlv_set_rx_update_nav),
|
SKW_MIB_SET_FORCE_RX_UPDATE_NAV);
|
}
|
|
static int skw_iwpriv_set_rx_update_nav(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_tlv_set_rx_update_nav nav = {0};
|
int temp_val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args %s\n", args);
|
|
p = args;
|
q = strchr(args, ',');
|
if(!p || !q)
|
{
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
} else {
|
skw_dbg("params found %s\n", p);
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("parameter error %d\n", temp_val);
|
return -EINVAL;
|
}
|
nav.intra_rssi = 0x80 | (0 - temp_val);
|
}
|
skw_dbg("intra rssi: %d\n", nav.intra_rssi);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if(!p || !p)
|
{
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
} else {
|
skw_dbg("params found %s\n", p + 1);
|
temp_val = simple_strtol(p + 1, NULL, 10);
|
if (temp_val >= 0 || temp_val < -255) {
|
skw_warn("parameter error %d\n", temp_val);
|
return -EINVAL;
|
}
|
nav.basic_rssi = 0x80 | (0 - temp_val);
|
}
|
skw_dbg("basic rssi: %d\n", nav.basic_rssi);
|
|
p = q;
|
p = q + strlen(",");
|
if(!p)
|
{
|
skw_warn("nav max time not found\n");
|
return -ENOTSUPP;
|
} else {
|
nav.nav_max_time = simple_strtol(p, NULL, 10);
|
}
|
skw_dbg("nav max time: %d\n", nav.nav_max_time);
|
|
ret = skw_set_rx_update_nav(iface->wdev.wiphy, iface->ndev, &nav);
|
|
if (!ret ) {
|
sprintf(resp, "set ok ");
|
} else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
//TLV 70
|
static int skw_set_apgo_timap(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_apgo_timap *timap)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, timap,
|
sizeof(struct skw_tlv_set_apgo_timap),
|
SKW_MIB_SET_APGO_TIMMAP);
|
}
|
|
static int skw_iwpriv_set_apgo_timap(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_tlv_set_apgo_timap timap = {0};
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("set_apgo_timap args %s\n", args);
|
|
p = args;
|
q = strchr(args, ',');
|
if (!p || !q) {
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
}
|
|
timap.dtimforce0 = (u8)skw_iwpriv_convert_string_to_u(p, q - p);
|
skw_dbg("dtim force0: %d\n", timap.dtimforce0);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !p) {
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
}
|
|
timap.dtimforce1 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
|
skw_dbg("dtim force1: %d\n", timap.dtimforce1);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p || !p) {
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
}
|
|
timap.timforce0 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
skw_dbg("tim force0: %d\n", timap.timforce0);
|
|
p = q;
|
q = strchr(p + 1, ',');
|
if (!p) {
|
skw_err("parameter error\n");
|
return -ENOTSUPP;
|
}
|
|
timap.timforce1 = (u8)skw_iwpriv_convert_string_to_u(p + 1, q - p - 1);
|
skw_dbg("tim force1: %d\n", timap.timforce1);
|
|
ret = skw_set_apgo_timap(iface->wdev.wiphy, iface->ndev, &timap);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
//TLV 71
|
static int skw_set_dbdc_disable(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_dbdc_disable *flag)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, flag,
|
sizeof(struct skw_tlv_set_dbdc_disable),
|
SKW_MIB_SET_DBDC_DISABLE);
|
}
|
|
static int skw_iwpriv_set_dbdc_disable(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
struct skw_tlv_set_dbdc_disable set_flag = {0};
|
u8 parmas_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (parmas_len == 1) {
|
ret = simple_strtol(args, NULL, 10);
|
if (!ret) {
|
skw_dbg("flag: %d\n", ret);
|
set_flag.disable = 0;
|
} else if (ret == 1) {
|
skw_dbg("flag: %d\n", ret);
|
set_flag.disable = 1;
|
} else {
|
goto SETFAIL;
|
}
|
} else if (parmas_len > 1) {
|
skw_err("params error\n");
|
goto SETFAIL;
|
}
|
|
skw_dbg("dbdc disable: %d\n", set_flag.disable);
|
|
ret = skw_set_dbdc_disable(iface->wdev.wiphy, iface->ndev, &set_flag);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
|
SETFAIL:
|
sprintf(resp, "set failed(param error)");
|
|
return ret;
|
}
|
|
//TLV 150
|
static int skw_set_assign_address_val(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_assign_addr_val *param)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, param,
|
sizeof(struct skw_tlv_set_assign_addr_val),
|
SKW_MIB_SET_ASSIGN_ADDRESS_VAL);
|
}
|
|
static int skw_iwpriv_set_assign_address_val(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_tlv_set_assign_addr_val params = {0};
|
u8 parmas_len = 0, remain_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
skw_warn("args %s\n", args);
|
|
p = args;
|
q = strchr(args, ',');
|
if (!p || !q) {
|
skw_err("params error\n");
|
return -ENOTSUPP;
|
}
|
|
params.addr = skw_iwpriv_convert_string_to_u32(p, q - p);
|
skw_dbg("addr: 0x%08x\n", params.addr);
|
|
p = q;
|
remain_len = parmas_len - (q - p) - 1;
|
if (!p) {
|
skw_err("param error\n");
|
return -ENOTSUPP;
|
}
|
|
params.val = skw_iwpriv_convert_string_to_u32(p + 1, remain_len);
|
skw_dbg("val: 0x%08x\n", params.val);
|
|
ret = skw_set_assign_address_val(iface->wdev.wiphy, iface->ndev, ¶ms);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
//TLV 250
|
static int skw_read_addr(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_get_assign_addr *param)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, param,
|
sizeof(struct skw_tlv_get_assign_addr),
|
SKW_MIB_GET_ASSIGN_ADDR_VAL_E);
|
}
|
|
static int skw_iwpriv_read_addr(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL;
|
struct skw_tlv_get_assign_addr params = {0};
|
u8 parmas_len = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
parmas_len = strlen(args);
|
if (!parmas_len || parmas_len > 10) {
|
skw_err("params error\n");
|
return -ENOTSUPP;
|
}
|
|
skw_dbg("args %s\n", args);
|
|
p = args;
|
if (!p) {
|
skw_err("params error\n");
|
return -ENOTSUPP;
|
}
|
|
params.addr = skw_iwpriv_convert_string_to_u32(p, parmas_len);
|
skw_dbg("addr: 0x%08x\n", params.addr);
|
|
ret = skw_read_addr(iface->wdev.wiphy, iface->ndev, ¶ms);
|
if (!ret)
|
sprintf(resp, "set ok ");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
}
|
|
//TLV 39 ageout thrd
|
//
|
static int skw_set_ageout_thrd(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_ageout_thrd *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_ageout_thrd), SKW_MIB_SET_AGEOUT_THOLD);
|
}
|
|
static int skw_iwpriv_set_ageout_thrd(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
struct skw_tlv_set_ageout_thrd thrd = {0};
|
u16 temp_val = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args %s\n", args);
|
|
p = args;
|
q = strchr(args, ',');
|
if (!p || !q) {
|
skw_err("param err\n");
|
return -ENOTSUPP;
|
}
|
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0xFF) {
|
skw_warn("parameter out of range (0 ~ 255) %d\n", temp_val);
|
thrd.ageout_kick_thrd = 0xFF;
|
} else {
|
thrd.ageout_kick_thrd = temp_val;
|
}
|
|
skw_dbg("tbtt cnt, kick out %d\n", thrd.ageout_kick_thrd);
|
|
p = q;
|
p = q + strlen(",");
|
if (!p) {
|
skw_warn("param err\n");
|
return -ENOTSUPP;
|
}
|
|
temp_val = simple_strtol(p, NULL, 10);
|
if (temp_val >= 0xFF) {
|
skw_warn("parameter out of range (0 ~ 255) %d\n", temp_val);
|
thrd.ageout_keep_alive_thrd = 0xFF;
|
} else {
|
thrd.ageout_keep_alive_thrd = temp_val;
|
}
|
|
skw_dbg("keep alive send null thrd: %d\n", thrd.ageout_keep_alive_thrd);
|
|
ret = skw_set_ageout_thrd(iface->wdev.wiphy, iface->ndev, &thrd);
|
|
if (!ret)
|
sprintf(resp, "set ok");
|
else
|
sprintf(resp, "set failed");
|
|
return ret;
|
|
}
|
|
//tlv 42
|
static int skw_set_reported_cqm_interval(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_report_cqm_rssi_low_itvl *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_report_cqm_rssi_low_itvl), SKW_MIB_SET_REPORT_CQM_RSSI_LOW_INT);
|
}
|
|
//tlv 54 ~ 57
|
static int skw_set_ap_new_channel(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_ap_new_channel *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_ap_new_channel), SKW_MIB_SET_AP_NEW_CHAN);
|
}
|
|
static int skw_set_tx_retry_limit_en(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_tx_retry_limit_en *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_tx_retry_limit_en), SKW_MIB_SET_TX_RETRY_LIMIT_EN);
|
}
|
|
static int skw_partial_twt_sched(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_partial_twt_sched *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_partial_twt_sched), SKW_MIB_SET_PARTIAL_TWT_SCHED);
|
}
|
|
static int skw_set_thm_thre(struct wiphy *wiphy, struct net_device *dev, struct skw_tlv_set_thm_thrd *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_thm_thrd), SKW_MIB_SET_THM_THRD);
|
}
|
|
static int skw_set_normal_scan_with_acs(struct wiphy *wiphy, struct net_device *dev,
|
struct skw_tlv_set_normal_scan_with_acs *params)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, params, sizeof(struct skw_tlv_set_normal_scan_with_acs),
|
SKW_MIB_SET_NORMAL_SCAN_WITH_ACS);
|
}
|
|
extern int skw_set_dot11r_mib(struct wiphy *wiphy, struct net_device *dev, bool enable);
|
|
static int skw_set_scan_send_probe_req_cnt(struct wiphy *wiphy, struct net_device *dev,
|
u8 cnt)
|
{
|
return skw_generic_tlv_operation(wiphy, dev, &cnt, 1,
|
SKW_MIB_SET_SCAN_SEND_PROBE_REQ_CNT);
|
}
|
|
static int skw_iwpriv_set_params(struct skw_iface *iface, void *param,
|
char *args, char *resp, int resp_len)
|
{
|
int ret = 0;
|
char *p = NULL, *q = NULL;
|
u32 params[16] = {0};
|
u32 params_num = 0;
|
struct skw_tlv_set_ap_new_channel new_chan = {0};
|
struct skw_tlv_set_tx_retry_limit_en txr_limit = {0};
|
struct skw_tlv_set_report_cqm_rssi_low_itvl itvl = {0};
|
struct skw_tlv_set_thm_thrd thrd = {0};
|
struct skw_tlv_set_partial_twt_sched twt_params = {0};
|
struct skw_tlv_set_normal_scan_with_acs normal_scan_params = {0};
|
u8 send_probe_req_cnt = 0;
|
|
if (!args)
|
return -EINVAL;
|
|
skw_dbg("args %s\n", args);
|
|
p = args;
|
|
while (p) {
|
params[params_num] = simple_strtol(p, NULL, 10);
|
params_num++;
|
|
q = strchr(p, ',');
|
if (!q) {
|
break;
|
}
|
|
p = q + 1; /* move past the comma */
|
}
|
|
if (params_num < 2) {
|
skw_warn("params error, at least 2\n");
|
return -ENOTSUPP;
|
}
|
|
switch (params[0]) {
|
case 1:
|
if (params_num != 6) {
|
skw_warn("params error, need 6 totally\n");
|
return -EINVAL;
|
}
|
|
new_chan.chan = params[1];
|
new_chan.center_chan = params[2];
|
new_chan.center_two_chan = params[3];
|
new_chan.bw = params[4];
|
new_chan.band = params[5];
|
|
ret = skw_set_ap_new_channel(iface->wdev.wiphy, iface->ndev, &new_chan);
|
skw_dbg("set ap new chan done\n");
|
|
break;
|
|
case 2:
|
if (params_num != 2) {
|
skw_warn("params error, need 2 totally\n");
|
return -EINVAL;
|
}
|
|
ret = skw_set_dot11r_mib(iface->wdev.wiphy, iface->ndev, (bool)params[1]);
|
skw_dbg("set 11r mib done\n");
|
break;
|
|
case 3:
|
if (params_num != 4) {
|
skw_warn("params error, need 4 totally\n");
|
return -EINVAL;
|
}
|
|
txr_limit.short_retry_check_en = !!params[1];
|
txr_limit.long_retry_check_en = !!params[2];
|
txr_limit.ampdu_retry_check_en = !!params[3];
|
|
ret = skw_set_tx_retry_limit_en(iface->wdev.wiphy, iface->ndev,&txr_limit);
|
skw_dbg("set tx retry limit done\n");
|
|
break;
|
|
case 4:
|
if (params_num != 3) {
|
skw_warn("params error, need 3 totally\n");
|
return -EINVAL;
|
}
|
|
itvl.report_cqm_low_intvl_min_dur = params[1];
|
itvl.report_cqm_low_intvl_max_dur = params[2];
|
|
ret = skw_set_reported_cqm_interval(iface->wdev.wiphy, iface->ndev,&itvl);
|
skw_dbg("set report cqm interval done\n");
|
break;
|
|
case 5:
|
if (params_num != 3) {
|
skw_warn("params error, need 3 totally\n");
|
return -EINVAL;
|
}
|
|
thrd.thm_high_thrd_tx_suspend = (s16)params[1];
|
thrd.thm_low_thrd_tx_resume = (s16)params[2];
|
|
ret = skw_set_thm_thre(iface->wdev.wiphy, iface->ndev, &thrd);
|
skw_dbg("set thm thre done\n");
|
|
break;
|
|
case 6:
|
if (params_num != 7) {
|
skw_warn("params error, need 7 totally\n");
|
return -EINVAL;
|
}
|
|
twt_params.en = params[1];
|
twt_params.start_time_l = params[2];
|
twt_params.start_time_h = params[3];
|
twt_params.interval = params[4];
|
twt_params.duration = params[5];
|
twt_params.duration_unit = params[6];
|
twt_params.sub_type = params[7];
|
|
ret = skw_partial_twt_sched(iface->wdev.wiphy, iface->ndev, &twt_params);
|
skw_dbg("set partial twt sched done\n");
|
|
break;
|
case 59:
|
if (params_num != 2) {
|
skw_warn("params error, need 2 totally\n");
|
return -EINVAL;
|
}
|
|
normal_scan_params.en = params[1];
|
ret = skw_set_normal_scan_with_acs(iface->wdev.wiphy, iface->ndev, &normal_scan_params);
|
skw_dbg("set normal scan with acs %d done\n", normal_scan_params.en);
|
|
break;
|
case 62:
|
if (params_num != 2) {
|
skw_warn("params error, need 2 totally \n");
|
return -EINVAL;
|
}
|
send_probe_req_cnt = params[1];
|
ret = skw_set_scan_send_probe_req_cnt(iface->wdev.wiphy, iface->ndev, send_probe_req_cnt);
|
skw_dbg("set scan send probe req cnt %d done\n", send_probe_req_cnt);
|
break;
|
|
default:
|
return -ENOTSUPP;
|
}
|
|
if (!ret)
|
snprintf(resp, resp_len, "set ok");
|
else
|
snprintf(resp, resp_len, "set failed");
|
|
return ret;
|
}
|
|
static struct skw_iwpriv_cmd skw_iwpriv_set_cmds[] = {
|
/* keep first */
|
{"help", skw_iwpriv_help, "usage"},
|
{"bandcfg", skw_iwpriv_set_bandcfg, "bandcfg=0/1/2"},
|
{"mppdudur", skw_iwpriv_set_max_ppdu_dur, "mppdudur=0~5,1~65535"},
|
{"edca", skw_iwpriv_set_edca_params, "edca=0,x(13 total)"},
|
{"ccanowifi", skw_iwpriv_set_cca_thre_nowifi, "ccanowifi=-u8"},
|
{"cca11b", skw_iwpriv_set_cca_thre_11b, "cca11b=-u8"},
|
{"ccaofdm", skw_iwpriv_set_cca_thre_ofdm, "ccaofdm=-u8"},
|
{"rtsrate", skw_iwpriv_set_force_rts_rate, "rtsrate=u8,u8,u8"},
|
{"rxrsprate", skw_iwpriv_set_force_rx_rsp_rate, "rxrsprate=u8,u8,u8,u8"},
|
{"scantime", skw_iwpriv_set_scan_time, "scantime=u8,u8"},
|
{"tcpdwhost", skw_iwpriv_set_tcpd_wakeup_host, "tcpdwhost=u8"},
|
{"rcminrate", skw_iwpriv_set_rc_min_rate, "rcminrate=u8"},
|
{"rcratechg", skw_iwpriv_set_rc_rate_change, "rcratechg=u8,u8,u8,u8,u8"},
|
{"rcsperate", skw_iwpriv_set_rc_spe_rate, "rcsperate=u8"},
|
{"txlftm", skw_iwpriv_set_tx_lifetime, "txlftm=u8"},
|
{"txrtycnt", skw_iwpriv_set_tx_retry_cnt, "txrtycnt=u8"},
|
{"txrtsthrd", skw_iwpriv_set_tx_rts_thrd, "txrtsthrd=u8"},
|
{"rxsped11frm", skw_iwpriv_set_rx_sepcial_80211_frame, "rxsped11frm=u8,u8,u8"},
|
{"rxupdnav", skw_iwpriv_set_rx_update_nav, "rxupdnav=-u8,-u8,u8"},
|
{"apgotimap", skw_iwpriv_set_apgo_timap, "apgotimap=u8,u8,u8,u8"},
|
{"dbdcdis", skw_iwpriv_set_dbdc_disable, "dbdcdis=u8"},
|
{"addrval", skw_iwpriv_set_assign_address_val, "addrval=0x12345678,0x12345678"},
|
{"rdaddr", skw_iwpriv_read_addr, "rdaddr=0x12345678"},
|
{"ageout", skw_iwpriv_set_ageout_thrd, "ageout=u8,u8"},
|
{"params", skw_iwpriv_set_params, "params=index,val1,val2,val3,..."}, //for tlv42 and 54~57,59
|
|
/*keep last*/
|
{NULL, NULL, NULL}
|
};
|
|
static struct skw_iwpriv_cmd skw_iwpriv_get_cmds[] = {
|
/* keep first */
|
{"help", skw_iwpriv_help, "usage"},
|
|
{"bandcfg", skw_iwpriv_get_bandcfg, "bandcfg"},
|
|
/*keep last*/
|
{NULL, NULL, NULL}
|
};
|
|
static struct skw_iwpriv_cmd *skw_iwpriv_cmd_match(struct skw_iwpriv_cmd *cmds,
|
const char *key, int key_len)
|
{
|
int i;
|
|
for (i = 0; cmds[i].name; i++) {
|
if (!memcmp(cmds[i].name, key, key_len))
|
return &cmds[i];
|
}
|
|
return NULL;
|
}
|
|
static int skw_iwpriv_set(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
int ret = 0;
|
int key_len;
|
char param[128] = {0};
|
char *token = NULL, *args = NULL;
|
struct skw_iwpriv_cmd *iwpriv_cmd;
|
struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev);
|
|
WARN_ON(sizeof(param) < wrqu->data.length);
|
|
if (copy_from_user(param, wrqu->data.pointer, sizeof(param))) {
|
skw_err("copy failed, length: %d\n",
|
wrqu->data.length);
|
|
return -EFAULT;
|
}
|
param[127] = '\0';
|
|
token = strchr(param, '=');
|
if (!token) {
|
key_len = strlen(param);
|
args = NULL;
|
} else {
|
key_len = token - param;
|
args = token + 1;
|
}
|
|
iwpriv_cmd = skw_iwpriv_cmd_match(skw_iwpriv_set_cmds, param, key_len);
|
if (iwpriv_cmd)
|
ret = iwpriv_cmd->handler(iface, iwpriv_cmd, args,
|
extra, SKW_GET_LEN_512);
|
else
|
ret = skw_iwpriv_help(iface, skw_iwpriv_set_cmds, NULL,
|
extra, SKW_GET_LEN_512);
|
|
if (ret < 0)
|
sprintf(extra, " usage: %s\n", iwpriv_cmd->help_info);
|
|
wrqu->data.length = SKW_GET_LEN_1024;
|
|
skw_dbg("resp: %s\n", extra);
|
|
return 0;
|
}
|
|
static int skw_iwpriv_get(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
int ret;
|
char cmd[128] = {0};
|
struct skw_iwpriv_cmd *priv_cmd = NULL;
|
struct skw_iface *iface = (struct skw_iface *)netdev_priv(dev);
|
|
if (copy_from_user(cmd, wrqu->data.pointer, sizeof(cmd))) {
|
skw_err("copy failed, length: %d\n",
|
wrqu->data.length);
|
|
return -EFAULT;
|
}
|
|
skw_dbg("cmd: 0x%x, %s(len: %d)\n", info->cmd, cmd, wrqu->data.length);
|
|
cmd[127] = '\0';
|
priv_cmd = skw_iwpriv_cmd_match(skw_iwpriv_get_cmds, cmd, strlen(cmd));
|
if (priv_cmd)
|
ret = priv_cmd->handler(iface, priv_cmd, NULL, extra,
|
SKW_GET_LEN_512);
|
else
|
ret = skw_iwpriv_help(iface, skw_iwpriv_get_cmds, NULL,
|
extra, SKW_GET_LEN_512);
|
|
wrqu->data.length = SKW_GET_LEN_512;
|
|
skw_dbg("resp: %s\n", extra);
|
|
return ret;
|
}
|
|
static int skw_iwpriv_at(struct net_device *dev,
|
struct iw_request_info *info,
|
union iwreq_data *wrqu, char *extra)
|
{
|
int ret;
|
char cmd[SKW_SET_LEN_256];
|
int len = wrqu->data.length;
|
struct skw_core *skw = ((struct skw_iface *)netdev_priv(dev))->skw;
|
|
BUG_ON(sizeof(cmd) < len);
|
|
if (copy_from_user(cmd, wrqu->data.pointer, sizeof(cmd))) {
|
skw_err("copy failed, length: %d\n", len);
|
|
return -EFAULT;
|
}
|
|
skw_dbg("cmd: %s, len: %d\n", cmd, len);
|
|
if (len + 2 > sizeof(cmd))
|
return -EINVAL;
|
|
cmd[len - 1] = 0xd;
|
cmd[len + 0] = 0xa;
|
cmd[len + 1] = 0x0;
|
|
ret = skw_send_at_cmd(skw, cmd, len + 2, extra, SKW_GET_LEN_512);
|
|
wrqu->data.length = SKW_GET_LEN_512;
|
|
skw_dbg("resp: %s", extra);
|
|
return ret;
|
}
|
|
static struct iw_priv_args skw_iw_priv_args[] = {
|
{
|
SKW_IW_PRIV_SET,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_1024,
|
"set",
|
},
|
{
|
SKW_IW_PRIV_GET,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"get",
|
},
|
{
|
SKW_IW_PRIV_AT,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_256,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"at",
|
},
|
{
|
SKW_IW_PRIV_80211MODE,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"mode",
|
},
|
{
|
SKW_IW_PRIV_GET_80211MODE,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_128,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"get_mode",
|
},
|
{
|
SKW_IW_PRIV_KEEP_ALIVE,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_1024,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"keep_alive",
|
},
|
{
|
SKW_IW_PRIV_WOW_FILTER,
|
IW_PRIV_TYPE_CHAR | SKW_SET_LEN_1024,
|
IW_PRIV_TYPE_CHAR | SKW_GET_LEN_512,
|
"wow_filter",
|
},
|
{0, 0, 0, {0} }
|
};
|
|
static const iw_handler skw_iw_priv_handlers[] = {
|
NULL,
|
skw_iwpriv_set,
|
NULL,
|
skw_iwpriv_get,
|
NULL,
|
skw_iwpriv_at,
|
skw_iwpriv_mode,
|
skw_iwpriv_get_mode,
|
skw_iwpriv_keep_alive,
|
skw_iwpriv_wow_filter,
|
//skw_iwpriv_get_wow_filter,
|
};
|
#endif
|
|
static const struct iw_handler_def skw_iw_ops = {
|
.standard = skw_iw_standard_handlers,
|
.num_standard = ARRAY_SIZE(skw_iw_standard_handlers),
|
#ifdef CONFIG_WEXT_PRIV
|
.private = skw_iw_priv_handlers,
|
.num_private = ARRAY_SIZE(skw_iw_priv_handlers),
|
.private_args = skw_iw_priv_args,
|
.num_private_args = ARRAY_SIZE(skw_iw_priv_args),
|
#endif
|
.get_wireless_stats = skw_get_wireless_stats,
|
};
|
|
const void *skw_iw_handlers(void)
|
{
|
#ifdef CONFIG_WIRELESS_EXT
|
return &skw_iw_ops;
|
#else
|
skw_info("CONFIG_WIRELESS_EXT not enabled\n");
|
return NULL;
|
#endif
|
}
|