mirror of
https://github.com/pvvx/ESP8266.git
synced 2025-03-31 20:29:59 +03:00
562 lines
16 KiB
C
562 lines
16 KiB
C
|
|
/*
|
|
* Copyright (c) 2011 Espressif System.
|
|
*
|
|
* MAC80211 support module
|
|
*/
|
|
#ifdef TEST_MODE
|
|
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/nl80211.h>
|
|
#include <linux/ieee80211.h>
|
|
#include <linux/slab.h>
|
|
#include <net/regulatory.h>
|
|
#include <net/cfg80211.h>
|
|
#include <net/mac80211.h>
|
|
#include <net/genetlink.h>
|
|
#include "esp_pub.h"
|
|
#include "esp_sip.h"
|
|
#include "esp_ctrl.h"
|
|
#include "esp_sif.h"
|
|
#include "esp_debug.h"
|
|
#include "esp_wl.h"
|
|
#include "testmode.h"
|
|
|
|
static u32 connected_nl;
|
|
static struct genl_info info_copy;
|
|
static struct esp_sip *sip_copy;
|
|
static u8 *sdio_buff;
|
|
|
|
#define SIP sip_copy
|
|
#define OUT_DONE() \
|
|
do { \
|
|
printk(KERN_DEBUG "esp_sdio: error occured in %s\n", __func__); \
|
|
} while(0)
|
|
|
|
/* ESP TEST netlinf family */
|
|
static struct genl_family test_genl_family = {
|
|
.id = GENL_ID_GENERATE,
|
|
.hdrsize = 0,
|
|
.name = "esp_sdio",
|
|
.version = 1,
|
|
.maxattr = TEST_ATTR_MAX,
|
|
};
|
|
|
|
struct loopback_param_s {
|
|
u32 packet_num;
|
|
u32 packet_id;
|
|
};
|
|
|
|
static struct loopback_param_s loopback_param;
|
|
u32 get_loopback_num()
|
|
{
|
|
return loopback_param.packet_num;
|
|
}
|
|
|
|
u32 get_loopback_id()
|
|
{
|
|
return loopback_param.packet_id;
|
|
}
|
|
|
|
void inc_loopback_id()
|
|
{
|
|
loopback_param.packet_id++;
|
|
}
|
|
|
|
#define REGISTER_REPLY(info) \
|
|
memcpy((char *)&info_copy, (char *)(info), sizeof(struct genl_info))
|
|
|
|
|
|
static void sip_send_test_cmd(struct esp_sip *sip, struct sk_buff *skb)
|
|
{
|
|
skb_queue_tail(&sip->epub->txq, skb);
|
|
|
|
#ifdef FPGA_LOOPBACK
|
|
queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
|
|
#else
|
|
ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
|
|
#endif /* FPGA_LOOPBACK */
|
|
|
|
}
|
|
|
|
static int esp_test_cmd_reply(struct genl_info *info, u32 cmd_type, char *reply_info)
|
|
{
|
|
struct sk_buff *skb;
|
|
void *hdr;
|
|
|
|
/*directly send ask_info to target, and waiting for report*/
|
|
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (skb == NULL)
|
|
goto out;
|
|
|
|
hdr = genlmsg_put(skb, info->snd_pid, info->snd_seq, &test_genl_family, 0, cmd_type);
|
|
if (hdr == NULL)
|
|
goto nla_put_failure;
|
|
|
|
NLA_PUT_STRING(skb, TEST_ATTR_STR, reply_info);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
genlmsg_reply(skb, info);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nlmsg_free(skb);
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_echo(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
char *echo_info;
|
|
int res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
connected_nl = info->snd_pid;
|
|
printk(KERN_DEBUG "esp_sdio: received a echo, "
|
|
"from pid %d\n", info->snd_pid);
|
|
sip_debug_show(SIP);
|
|
|
|
/*get echo info*/
|
|
echo_info = nla_data(info->attrs[TEST_ATTR_STR]);
|
|
|
|
res=esp_test_cmd_reply(info, TEST_CMD_ECHO, echo_info);
|
|
return res;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_sdiospeed(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
char *speed_info;
|
|
int res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
connected_nl = info->snd_pid;
|
|
|
|
/*get echo info*/
|
|
speed_info = nla_data(info->attrs[TEST_ATTR_STR]);
|
|
|
|
printk(KERN_DEBUG "esp_sdio: received a sdio speed %s, "
|
|
"from pid %d\n", speed_info, info->snd_pid);
|
|
|
|
if (!strcmp(speed_info, "high")) {
|
|
sif_platform_target_speed(1);
|
|
} else if (!strcmp(speed_info, "low")) {
|
|
sif_platform_target_speed(0);
|
|
} else {
|
|
printk(KERN_DEBUG "%s: %s unsupported\n", __func__, speed_info);
|
|
}
|
|
|
|
res=esp_test_cmd_reply(info, TEST_CMD_SDIOSPEED, speed_info);
|
|
return res;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_ask(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
char *ask_info;
|
|
int res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
/*get echo info*/
|
|
ask_info = nla_data(info->attrs[TEST_ATTR_STR]);
|
|
|
|
/*directly send ask_info to target, and waiting for report*/
|
|
res=esp_test_cmd_reply(info, TEST_CMD_ASK, "ok");
|
|
return res;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_sleep(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
struct sip_cmd_sleep *sleepcmd;
|
|
struct sk_buff *skb = NULL;
|
|
int res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
skb = sip_alloc_ctrl_skbuf(SIP, sizeof(struct sip_cmd_sleep), SIP_CMD_SLEEP);
|
|
if (!skb)
|
|
goto out;
|
|
|
|
sleepcmd = (struct sip_cmd_sleep *)(skb->data + sizeof(struct sip_tx_info));
|
|
sleepcmd->sleep_mode = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
sleepcmd->sleep_tm_ms = nla_get_u32(info->attrs[TEST_ATTR_PARA1]);
|
|
sleepcmd->wakeup_tm_ms = nla_get_u32(info->attrs[TEST_ATTR_PARA2]);
|
|
sleepcmd->sleep_times = nla_get_u32(info->attrs[TEST_ATTR_PARA3]);
|
|
|
|
sip_send_test_cmd(SIP, skb);
|
|
|
|
/*directly send ask_info to target, and waiting for report*/
|
|
res=esp_test_cmd_reply(info, TEST_CMD_SLEEP, "ok");
|
|
return res;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_wakeup(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
struct sip_cmd_wakeup *wakeupcmd;
|
|
struct sk_buff *skb = NULL;
|
|
//int res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
skb = sip_alloc_ctrl_skbuf(SIP, sizeof(struct sip_cmd_wakeup), SIP_CMD_WAKEUP);
|
|
if (!skb)
|
|
goto out;
|
|
wakeupcmd = (struct sip_cmd_wakeup *)(skb->data + sizeof(struct sip_tx_info));
|
|
wakeupcmd->check_data = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
|
|
/*directly send reply_info to target, and waiting for report*/
|
|
REGISTER_REPLY(info);
|
|
//res=esp_test_cmd_reply(info, TEST_CMD_WAKEUP, "ok");
|
|
|
|
sip_send_test_cmd(SIP, skb);
|
|
return 0;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_loopback(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
u32 txpacket_len;
|
|
u32 rxpacket_len;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
txpacket_len = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
rxpacket_len = nla_get_u32(info->attrs[TEST_ATTR_PARA1]);
|
|
loopback_param.packet_num = nla_get_u32(info->attrs[TEST_ATTR_PARA2]);
|
|
loopback_param.packet_id=0;
|
|
REGISTER_REPLY(info);
|
|
return sip_send_loopback_mblk(SIP, txpacket_len, rxpacket_len, 0);
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
u8 probe_req_frm[] = {0x40,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x03,0x8F,0x11,0x22,0x88,
|
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x08,0x82,0x84,0x8B,0x96,
|
|
0x0C,0x12,0x18,0x24,0x32,0x04,0x30,0x48,0x60,0x6C
|
|
};
|
|
*/
|
|
|
|
static int sip_send_tx_frame(struct esp_sip *sip, u32 packet_len)
|
|
{
|
|
|
|
struct sk_buff *skb = NULL;
|
|
u8 *ptr = NULL;
|
|
int i;
|
|
|
|
skb = alloc_skb(packet_len, GFP_KERNEL);
|
|
skb->len = packet_len;
|
|
ptr = skb->data;
|
|
/* fill up pkt payload */
|
|
for (i = 0; i < skb->len; i++) {
|
|
ptr[i] = i;
|
|
}
|
|
#ifdef FPGA_LOOPBACK
|
|
skb_queue_tail(&sip->epub->txq, skb);
|
|
queue_work(sip->epub->esp_wkq, &sip->epub->tx_work);
|
|
#else
|
|
sip_tx_data_pkt_enqueue(sip->epub, skb);
|
|
ieee80211_queue_work(sip->epub->hw, &sip->epub->tx_work);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int esp_test_tx(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
u32 txpacket_len;
|
|
u32 res;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
txpacket_len = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
REGISTER_REPLY(info);
|
|
sip_send_tx_frame(SIP, txpacket_len);
|
|
res=esp_test_cmd_reply(info, TEST_CMD_TX, "tx out");
|
|
return res;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_genl(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
struct sip_cmd_debug *dbgcmd;
|
|
struct sk_buff *skb = NULL;
|
|
int i;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
skb = sip_alloc_ctrl_skbuf(SIP, sizeof(struct sip_cmd_debug), SIP_CMD_DEBUG);
|
|
if (!skb)
|
|
goto out;
|
|
|
|
dbgcmd = (struct sip_cmd_debug *)(skb->data + sizeof(struct sip_hdr));
|
|
dbgcmd->cmd_type = nla_get_u32(info->attrs[TEST_ATTR_CMD_TYPE]);
|
|
dbgcmd->para_num = nla_get_u32(info->attrs[TEST_ATTR_PARA_NUM]);
|
|
printk(KERN_DEBUG "%s dbgcmdType %d paraNum %d\n", __func__, dbgcmd->cmd_type, dbgcmd->para_num);
|
|
for (i=0; i<dbgcmd->para_num; i++)
|
|
dbgcmd->para[i] = nla_get_u32(info->attrs[TEST_ATTR_PARA(i)]);
|
|
|
|
/*directly send reply_info to target, and waiting for report*/
|
|
REGISTER_REPLY(info);
|
|
|
|
sip_send_test_cmd(SIP, skb);
|
|
return 0;
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_sdio_wr(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
int res;
|
|
u32 func_no, addr, value;
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
func_no = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
addr = nla_get_u32(info->attrs[TEST_ATTR_PARA1]);
|
|
value = nla_get_u32(info->attrs[TEST_ATTR_PARA2]);
|
|
|
|
if(!func_no) {
|
|
sdio_io_writeb( SIP->epub, (u8) value&0xff, addr, &res);
|
|
} else {
|
|
memcpy(sdio_buff, (u8 *)&value, 4);
|
|
res=sif_io_sync(SIP->epub, addr, sdio_buff, 4, SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
|
|
}
|
|
|
|
/*directly send reply_info to target, and waiting for report*/
|
|
REGISTER_REPLY(info);
|
|
if (!res)
|
|
esp_test_cmd_reply(info, TEST_CMD_SDIO_WR, "write ok!");
|
|
else
|
|
esp_test_cmd_reply(info, TEST_CMD_SDIO_WR, "write fail!");
|
|
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int esp_test_sdio_rd(struct sk_buff *skb_2,
|
|
struct genl_info *info)
|
|
{
|
|
int res;
|
|
u32 func_no, addr, value;
|
|
char value_str[12];
|
|
|
|
if (info == NULL)
|
|
goto out;
|
|
|
|
func_no = nla_get_u32(info->attrs[TEST_ATTR_PARA0]);
|
|
addr = nla_get_u32(info->attrs[TEST_ATTR_PARA1]);
|
|
|
|
if(!func_no) {
|
|
memset(sdio_buff, 0, 4);
|
|
sdio_buff[0]= sdio_io_readb(SIP->epub, addr, &res);
|
|
} else {
|
|
res=sif_io_sync(SIP->epub, addr, sdio_buff, 4, SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
|
|
}
|
|
memcpy((u8 *)&value, sdio_buff, 4);
|
|
|
|
/*directly send reply_info to target, and waiting for report*/
|
|
REGISTER_REPLY(info);
|
|
if (!res) {
|
|
sprintf((char *)&value_str, "0x%x", value);
|
|
esp_test_cmd_reply(info, TEST_CMD_SDIO_RD, value_str);
|
|
} else
|
|
esp_test_cmd_reply(info, TEST_CMD_SDIO_RD, "read fail!");
|
|
|
|
out:
|
|
OUT_DONE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* TEST_CMD netlink policy */
|
|
|
|
static struct nla_policy test_genl_policy[TEST_ATTR_MAX + 1] = {
|
|
[TEST_ATTR_CMD_NAME]= { .type = NLA_NUL_STRING, .len = GENL_NAMSIZ - 1 },
|
|
[TEST_ATTR_CMD_TYPE] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA_NUM] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA0] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA1] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA2] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA3] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA4] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA5] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA6] = { .type = NLA_U32 },
|
|
[TEST_ATTR_PARA7] = { .type = NLA_U32 },
|
|
[TEST_ATTR_STR] = { .type = NLA_NUL_STRING, .len = 256-1 },
|
|
};
|
|
|
|
/* Generic Netlink operations array */
|
|
static struct genl_ops esp_test_ops[] = {
|
|
{
|
|
.cmd = TEST_CMD_ECHO,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_echo,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_ASK,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_ask,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_SLEEP,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_sleep,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_WAKEUP,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_wakeup,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_LOOPBACK,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_loopback,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_TX,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_tx,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_DEBUG,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_genl,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_SDIO_WR,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_sdio_wr,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_SDIO_RD,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_sdio_rd,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = TEST_CMD_SDIOSPEED,
|
|
.policy = test_genl_policy,
|
|
.doit = esp_test_sdiospeed,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
};
|
|
|
|
static int esp_test_netlink_notify(struct notifier_block *nb,
|
|
unsigned long state,
|
|
void *_notify)
|
|
{
|
|
struct netlink_notify *notify = _notify;
|
|
|
|
if (state != NETLINK_URELEASE)
|
|
return NOTIFY_DONE;
|
|
|
|
if (notify->pid == connected_nl) {
|
|
printk(KERN_INFO "esp_sdio: user released netlink"
|
|
" socket \n");
|
|
connected_nl = 0;
|
|
}
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
static struct notifier_block test_netlink_notifier = {
|
|
.notifier_call = esp_test_netlink_notify,
|
|
};
|
|
|
|
int test_init_netlink(struct esp_sip *sip)
|
|
{
|
|
int rc;
|
|
printk(KERN_INFO "esp_sdio: initializing netlink\n");
|
|
|
|
sip_copy=sip;
|
|
/* temp buffer for sdio test */
|
|
sdio_buff = kzalloc(8, GFP_KERNEL);
|
|
|
|
rc = genl_register_family_with_ops(&test_genl_family,
|
|
esp_test_ops, ARRAY_SIZE(esp_test_ops));
|
|
if (rc)
|
|
goto failure;
|
|
|
|
rc = netlink_register_notifier(&test_netlink_notifier);
|
|
if (rc)
|
|
goto failure;
|
|
|
|
return 0;
|
|
|
|
failure:
|
|
printk(KERN_DEBUG "esp_sdio: error occured in %s\n", __func__);
|
|
kfree(sdio_buff);
|
|
return -EINVAL;
|
|
}
|
|
|
|
void test_exit_netlink(void)
|
|
{
|
|
int ret;
|
|
|
|
if (sdio_buff == NULL)
|
|
return;
|
|
|
|
kfree(sdio_buff);
|
|
printk(KERN_INFO "esp_sdio: closing netlink\n");
|
|
/* unregister the notifier */
|
|
netlink_unregister_notifier(&test_netlink_notifier);
|
|
/* unregister the family */
|
|
ret = genl_unregister_family(&test_genl_family);
|
|
if (ret)
|
|
printk(KERN_DEBUG "esp_sdio: "
|
|
"unregister family %i\n", ret);
|
|
}
|
|
void esp_test_cmd_event(u32 cmd_type, char *reply_info)
|
|
{
|
|
esp_test_cmd_reply(&info_copy, cmd_type, reply_info);
|
|
}
|
|
#endif //ifdef TEST_MODE
|
|
|