heyuanjie87 před 6 roky
revize
76ccdcd6df
14 změnil soubory, kde provedl 2567 přidání a 0 odebrání
  1. 19 0
      README.md
  2. 17 0
      SConscript
  3. 836 0
      airkiss.c
  4. 253 0
      airkiss.h
  5. 205 0
      doc/cap1.txt
  6. 55 0
      doc/test-report.md
  7. 40 0
      osdep/qt/airkiss.pro
  8. 197 0
      osdep/qt/akWorker.cpp
  9. 41 0
      osdep/qt/akWorker.h
  10. 11 0
      osdep/qt/main.cpp
  11. 118 0
      osdep/qt/mainwindow.cpp
  12. 40 0
      osdep/qt/mainwindow.h
  13. 234 0
      osdep/qt/mainwindow.ui
  14. 501 0
      osdep/rtthread/airkiss_demo.c

+ 19 - 0
README.md

@@ -0,0 +1,19 @@
+## 腾讯 WiFi设备一键配网协议
+
+# 启用(in rt-thread)
+```
+//rtconfig.h
+
+#define PKG_USING_AIRKISS_OPEN
+#define AIRKISS_OPEN_DEMO_ENABLE /* airkiss应用示例 */
+```
+
+# 示例
+- [在rt-thread中应用](osdep/rtthread/airkiss_demo.c)
+- [电脑模拟测试](osdep/qt/akWorker.cpp)
+
+# 参考
+- [博客文章](https://blog.csdn.net/lb5761311/article/details/77945848)
+
+# 测试结果
+- [报告](doc/test-report.md)

+ 17 - 0
SConscript

@@ -0,0 +1,17 @@
+Import('RTT_ROOT')
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd]
+
+src = Split('''
+        airkiss.c
+        ''')
+
+if GetDepend('AIRKISS_OPEN_DEMO_ENABLE'):
+    src += ['osdep/rtthread/airkiss_demo.c']
+
+group = DefineGroup('airkissOpen', src, depend = ['PKG_USING_AIRKISS_OPEN'], CPPPATH = CPPPATH)
+
+Return('group')

+ 836 - 0
airkiss.c

@@ -0,0 +1,836 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author           Notes
+ * 2019-05-16     heyuanjie87      first version
+*/
+
+#include "airkiss.h"
+
+#include <string.h>
+#include <stdint.h>
+
+#ifdef AIRKISS_LOG_ENABLE
+#define AKLOG_D                     \
+    if (lc->cfg && lc->cfg->printf) \
+    lc->cfg->printf
+#else
+#define AKLOG_D(...)
+#endif
+
+#define AKSTATE_WFG 0
+#define AKSTATE_WFM 1
+#define AKSTATE_WFP 2
+#define AKSTATE_WFD 4
+#define AKSTATE_CMP 5
+
+typedef struct
+{
+    uint16_t val[6];
+    uint8_t pos;
+    uint8_t scnt;
+    uint8_t err;
+} akcode_t;
+
+typedef struct
+{
+    uint8_t val[4];
+    uint8_t pos : 4;
+    uint8_t scnt : 4;
+    uint8_t err;
+    uint16_t icnt;
+    uint8_t sa[12];
+} akcode_guide_t;
+
+typedef struct
+{
+    union {
+        akcode_guide_t code1[3];
+        akcode_t code2[2];
+    } uc;
+
+    uint8_t reclen;
+    uint8_t state;
+    uint8_t nossid;
+    char seq[16];
+
+    uint8_t data[66];
+    uint8_t random;
+    uint8_t baselen;
+    uint8_t prslen;
+    uint8_t ssidcrc;
+    uint8_t pwdlen;
+    uint8_t pwdcrc;
+    const airkiss_config_t *cfg;
+} akloc_context_t;
+
+#define AKLOC_CODE1(x, i) ((x)->uc.code1[i])
+#define AKLOC_CODE2(x) (&(x)->uc.code2[0])
+#define AKLOC_CODE3(x) (&(x)->uc.code2[1])
+
+unsigned char airkiss_crc8(unsigned char *message, unsigned char len)
+{
+    uint8_t crc = 0;
+    uint8_t i;
+
+    while (len--)
+    {
+        crc ^= *message++;
+        for (i = 0; i < 8; i++)
+        {
+            if (crc & 0x01)
+                crc = (crc >> 1) ^ 0x8c;
+            else
+                crc >>= 1;
+        }
+    }
+
+    return crc;
+}
+
+static void akloc_reset(akloc_context_t *lc)
+{
+    lc->state = 0;
+    lc->reclen = 0;
+    lc->baselen = 0;
+    memset(lc->seq, 0xff, sizeof(lc->seq));
+    memset(&lc->uc, 0, sizeof(lc->uc));
+}
+
+static uint8_t akinfo_getu8(uint16_t v[2])
+{
+    uint8_t ret = 0;
+
+    ret = ((v[0] & 0xF) << 4) | (v[1] & 0xF);
+
+    return ret;
+}
+
+static uint16_t aklen_udp(akloc_context_t *lc, uint16_t len)
+{
+    return (len - lc->baselen);
+}
+
+static int aklen_input(akloc_context_t *lc, akcode_t *ac, int fmlen, int num)
+{
+    if (ac->pos < num)
+    {
+        ac->val[ac->pos] = aklen_udp(lc, fmlen);
+        ac->pos++;
+    }
+
+    return (ac->pos == num);
+}
+
+static void ak_codemve(akcode_t *ac, int pos, int n)
+{
+    int i;
+
+    for (i = 0; i < n; i++)
+    {
+        ac->val[i] = ac->val[pos + i];
+    }
+    ac->pos = n;
+}
+
+static int akseq_num(akloc_context_t *lc)
+{
+    int n;
+
+    n = lc->prslen - lc->reclen;
+    if (n > 4)
+        n = 6;
+    else if (n != 0)
+        n += 2;
+
+    return n;
+}
+
+static int akseq_input(akloc_context_t *lc, uint8_t *d, int n, int seqi)
+{
+    int i;
+    int ret;
+
+    if (lc->seq[seqi] != seqi)
+    {
+        int pos;
+
+        lc->seq[seqi] = seqi;
+        n -= 2;
+        pos = seqi * 4;
+
+        for (i = 0; i < n; i++)
+        {
+            lc->data[pos] = d[i];
+            lc->reclen++;
+            pos++;
+        }
+    }
+
+    ret = lc->prslen - lc->reclen;
+
+    return ret;
+}
+
+static int ak_is_guidefield(akcode_t *ac)
+{
+    int ret = 0;
+
+    if ((ac->val[1] - ac->val[0] == 1) &&
+        (ac->val[2] - ac->val[0] == 2) &&
+        (ac->val[3] - ac->val[0] == 3))
+    {
+        ret = 1;
+    }
+
+    return ret;
+}
+
+/*
+  在接收magicfield阶段检查是否收到乱序的guide code
+*/
+static int ak_is_guidefield_errseq(akcode_t *ac)
+{
+    int ret = 0;
+
+    if ((ac->val[0] < 5) &&
+        (ac->val[1] < 5) &&
+        (ac->val[2] < 5) &&
+        (ac->val[3] < 5))
+    {
+        ret = 1;
+    }
+
+    return ret;
+}
+
+/*
+  在4个包中从指定位置找出符合magicfield的序列
+*/
+static int ak_is_magicfield_corseq(akcode_t *ac, int pos, int n)
+{
+    int ret;
+    uint8_t v[4];
+    uint8_t vd[4] = {0, 1, 2, 3};
+    int i;
+
+    for (i = 0; i < n; i++)
+    {
+        v[i] = ac->val[pos + i] >> 4;
+    }
+    ret = !memcmp(v, vd, n);
+
+    return ret;
+}
+
+/*
+  检查是否是错误的magicfield序列
+*/
+static int ak_is_magicfield_errseq(akcode_t *ac)
+{
+    int i;
+
+    for (i = 0; i < 4; i++)
+    {
+        if ((ac->val[i] >> 4) > 3)
+            break;
+    }
+
+    return (i == 4);
+}
+
+/*
+  剔除错误的magicfield序列
+*/
+static int ak_realign_magicfield(akcode_t *ac)
+{
+    int r = 1;
+
+    while (r < 4)
+    {
+        if (ak_is_magicfield_corseq(ac, r, 4 - r))
+        {
+            ak_codemve(ac, r, 4 - r);
+            r = -1;
+            break;
+        }
+
+        r++;
+    }
+
+    return (r == -1);
+}
+
+static int ak_get_magicfield(akloc_context_t *lc, akcode_t *ac)
+{
+    int ret;
+
+    if ((ret = ak_is_magicfield_corseq(ac, 0, 4)) != 0)
+    {
+        if (ac->val[0] == 8)
+            ac->val[0] = 0;
+        lc->prslen = akinfo_getu8(&ac->val[0]);
+        lc->ssidcrc = akinfo_getu8(&ac->val[2]);
+    }
+
+    return ret;
+}
+
+static int ak_is_prefixfield_corseq(akcode_t *ac, int pos, int n)
+{
+    uint8_t v[4];
+    uint8_t vd[4] = {4, 5, 6, 7};
+    int i;
+    int ret;
+
+    for (i = 0; i < n; i++)
+    {
+        v[i] = ac->val[pos + i] >> 4;
+    }
+
+    ret = !memcmp(v, vd, n);
+
+    return ret;
+}
+
+static int ak_get_prefixfield(akloc_context_t *lc, akcode_t *ac)
+{
+    int ret;
+
+    if ((ret = ak_is_prefixfield_corseq(ac, 0, 4)) != 0)
+    {
+        lc->pwdlen = akinfo_getu8(&ac->val[0]);
+        lc->pwdcrc = akinfo_getu8(&ac->val[2]);
+    }
+
+    return ret;
+}
+
+static int ak_realign_prefixfield(akcode_t *ac)
+{
+    int r;
+
+    r = 1;
+    while (r < 4)
+    {
+        if (ak_is_prefixfield_corseq(ac, r, 4 - r))
+        {
+            ak_codemve(ac, r, 4 - r);
+            r = -1;
+            break;
+        }
+
+        r++;
+    }
+
+    return (r == -1);
+}
+
+static int ak_is_datafield_corseq(akcode_t *ac, int pos, int n)
+{
+    int ret;
+    int i;
+    uint8_t v[6];
+    uint8_t vd[6] = {1, 1, 2, 2, 2, 2};
+
+    for (i = 0; i < n; i++)
+    {
+        if (i < 2)
+            v[i] = ac->val[i + pos] >> 7;
+        else
+            v[i] = (ac->val[i + pos] & 0x100) >> 7;
+    }
+
+    ret = !memcmp(v, vd, n);
+
+    return ret;
+}
+
+static int ak_get_datafield(akcode_t *ac, uint8_t data[4], int *seqi, int n)
+{
+    int i = 0;
+    int fail = 1;
+    uint8_t tmp[6] = {0};
+
+    if (n < 3 || n > 6)
+        goto _out;
+    if (!ak_is_datafield_corseq(ac, 0, n))
+        goto _out;
+
+    tmp[0] = ac->val[i++] & 0x7F;
+    tmp[1] = ac->val[i++] & 0x7F;
+    tmp[2] = ac->val[i++] & 0xFF;
+    tmp[3] = ac->val[i++] & 0xFF;
+    tmp[4] = ac->val[i++] & 0xFF;
+    tmp[5] = ac->val[i++] & 0xFF;
+
+    fail = ((airkiss_crc8(&tmp[1], n - 1) & 0x7F) != tmp[0]);
+    if (!fail)
+    {
+        memcpy(data, &tmp[2], 4);
+        *seqi = tmp[1];
+    }
+
+_out:
+    return (!fail);
+}
+
+static int ak_realign_datafield(akcode_t *ac)
+{
+    int r = 1;
+
+    while (r < 6)
+    {
+        if (ak_is_datafield_corseq(ac, r, 6 - r))
+        {
+            ak_codemve(ac, r, 6 - r);
+            r = -1;
+            break;
+        }
+
+        r++;
+    }
+
+    return (r == -1);
+}
+
+static akcode_guide_t *ak_guide_getcode(akloc_context_t *lc, unsigned char *f)
+{
+    akcode_guide_t *ac;
+
+    if (f == NULL) /* 是模拟测试 */
+    {
+        ac = &AKLOC_CODE1(lc, 2);
+    }
+    else
+    {
+        unsigned char *sa;
+        unsigned i;
+        int found = 0;
+        akcode_guide_t *imin;
+
+        sa = f + 10;
+        imin = &AKLOC_CODE1(lc, 0);
+        ac = imin;
+        for (i = 0; i < sizeof(lc->uc.code1) / sizeof(lc->uc.code1[0]); i++)
+        {
+            /* 匹配地址 */
+            found = !memcmp(ac->sa, sa, sizeof(ac->sa));
+            if (found)
+                break;
+            /* 记录输入最少的 */
+            if (ac->icnt < imin->icnt)
+                imin = ac;
+            ac++;
+        }
+
+        if (!found)
+        {
+            /* 淘汰输入最少的 */
+            ac = imin;
+            ac->pos = 0;
+            ac->err = 0;
+            ac->scnt = 0;
+            ac->icnt = 0;
+            memcpy(ac->sa, sa, sizeof(ac->sa));
+        }
+    }
+
+    return ac;
+}
+
+static int ak_guidefield_input(akcode_guide_t *ac, uint16_t len)
+{
+    if (ac->pos < 4)
+    {
+        if ((ac->pos != 0) && ((len - ac->val[ac->pos - 1]) != 1))
+        {
+            ac->pos = 0;
+            if (ac->icnt > 0)
+                ac->icnt--;
+        }
+
+        ac->val[ac->pos] = len;
+        ac->pos++;
+        ac->icnt += ac->pos;
+    }
+
+    return (ac->pos == 4);
+}
+
+static int ak_waitfor_guidefield(akloc_context_t *lc, uint8_t *f, uint16_t len)
+{
+    int ret = AIRKISS_STATUS_CONTINUE;
+    akcode_guide_t *ac;
+
+    ac = ak_guide_getcode(lc, f);
+
+    if (ak_guidefield_input(ac, len))
+    {
+        ac->pos = 0;
+        ac->scnt++;
+
+        /* 至少两次相同的guide code才算获取成功 */
+        if ((ac->scnt >= 2) && ac->icnt >= 20)
+        {
+            lc->state = AKSTATE_WFM;
+            lc->baselen = ac->val[0] - 1;
+            ac->scnt = 0;
+            ac->err = 0;
+            ac->icnt = 0;
+
+            AKLOG_D("guide baselen %d\n", lc->baselen);
+        }
+    }
+
+    if (lc->state == AKSTATE_WFM)
+    {
+        if (ac != &AKLOC_CODE1(lc, 2))
+        {
+            memcpy(AKLOC_CODE1(lc, 2).sa, ac->sa, sizeof(ac->sa));
+        }
+        memset(AKLOC_CODE2(lc), 0, sizeof(akcode_t) * 2);
+        ret = AIRKISS_STATUS_CHANNEL_LOCKED;
+    }
+
+    return ret;
+}
+
+static int ak_waitfor_magicfield(akloc_context_t *lc, uint16_t len)
+{
+    int ret = AIRKISS_STATUS_CONTINUE;
+    akcode_t *ac = AKLOC_CODE2(lc);
+
+    if (aklen_input(lc, ac, len, 4))
+    {
+        ac->pos = 0;
+
+        if (ak_get_magicfield(lc, ac))
+        {
+            lc->state = AKSTATE_WFP;
+            ac->err = 0;
+
+            AKLOG_D("magic: prslen(%d) ssidcrc(%X)\n", lc->prslen, lc->ssidcrc);
+        }
+        else if (ak_is_guidefield(ac) ||
+                 ak_is_guidefield_errseq(ac))
+        {
+            /* 收到的还是或乱序的guidecode则忽略 */
+        }
+        else if (ak_realign_magicfield(ac))
+        {
+            /* 保留符合要求的序列 */
+        }
+        else
+        {
+            if (ac->err++ > 6)
+            {
+                akloc_reset(lc);
+                AKLOG_D("airkiis reset from magic\n");
+            }
+        }
+    }
+
+    return ret;
+}
+
+static int ak_waitfor_prefixfield(akloc_context_t *lc, uint16_t len)
+{
+    int ret = AIRKISS_STATUS_CONTINUE;
+    akcode_t *ac = AKLOC_CODE2(lc);
+
+    if (aklen_input(lc, ac, len, 4))
+    {
+        ac->pos = 0;
+
+        if (ak_get_prefixfield(lc, ac))
+        {
+            lc->state = AKSTATE_WFD;
+            ac->err = 0;
+
+            AKLOG_D("prefix: pwdlen(%d) pwdcrc(%X)\n", lc->pwdlen, lc->pwdcrc);
+        }
+        else if (ak_is_magicfield_corseq(ac, 0, 4) ||
+                 ak_is_magicfield_errseq(ac))
+        {
+            /* 收到magicfield 忽略 */
+        }
+        else if (ak_realign_prefixfield(ac))
+        {
+            /* 保留符合要求的 */
+        }
+        else
+        {
+            if (ac->err++ > 5)
+            {
+                akloc_reset(lc);
+                AKLOG_D("airkiss reset from prefix");
+            }
+        }
+    }
+
+    return ret;
+}
+
+/*
+  只判断密码和random是否收完
+*/
+static int ak_is_pwdrand_complete(akloc_context_t *lc)
+{
+    int ret = 0;
+    unsigned i;
+    int n = 0;
+
+    for (i = 0; i < (sizeof(lc->seq) / sizeof(lc->seq[0])); i++)
+    {
+        if (lc->seq[i] == 0xff)
+            break;
+
+        n += 4;
+        if (n >= (lc->pwdlen + 1))
+        {
+            ret = 1;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static int _datafield_input(akloc_context_t *lc, akcode_t *ac, uint16_t len, int n, int nossid)
+{
+    int ret = 0;
+
+    if (n && aklen_input(lc, ac, len, n))
+    {
+        uint8_t data[4];
+        int seqi;
+
+        ac->pos = 0;
+
+        if ((ret = ak_get_datafield(ac, data, &seqi, n)) == 1)
+        {
+            if (akseq_input(lc, data, n, seqi) == 0)
+            {
+                lc->state = AKSTATE_CMP;
+
+                AKLOG_D("data complete %d\n", n);
+            }
+            else if (nossid && ak_is_pwdrand_complete(lc))
+            {
+                lc->state = AKSTATE_CMP;
+
+                AKLOG_D("data nossid complete\n");
+            }
+        }
+        else
+        {
+            if (!ak_realign_datafield(ac))
+            {
+                ac->err++;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static int ak_waitfor_datafield(akloc_context_t *lc, uint16_t len, int nossid)
+{
+    int ret = AIRKISS_STATUS_CONTINUE;
+    akcode_t *ac = AKLOC_CODE2(lc);
+    int n;
+    uint16_t udplen;
+
+    udplen = aklen_udp(lc, len);
+    if (udplen < 0x80)
+    {
+        return ret;
+    }
+
+    n = lc->prslen & 0x03;
+    if (n && (lc->prslen > 8) && !nossid)
+    {
+        akcode_t *ac3 = AKLOC_CODE3(lc);
+
+        if (_datafield_input(lc, ac3, len, n + 2, 0))
+        {
+            if ((n = akseq_num(lc)) == 0)
+                goto _out;
+            ac->pos = 0;
+
+            return ret;
+        }
+    }
+
+    /* 期望当前序列的包数(6, <6, 0) */
+    n = akseq_num(lc);
+
+    _datafield_input(lc, ac, len, n, nossid);
+    if (ac->err > 20)
+    {
+        akloc_reset(lc);
+        AKLOG_D("airkiss reset from data\n");
+    }
+
+_out:
+    if ((lc->state == AKSTATE_CMP) || (n == 0))
+    {
+        lc->nossid = nossid;
+        lc->state = AKSTATE_CMP;
+        ret = AIRKISS_STATUS_COMPLETE;
+    }
+
+    return ret;
+}
+
+static int ak_sa_filter(akloc_context_t *lc, uint8_t *f)
+{
+    unsigned char *sa;
+
+    sa = f + 10;
+    return memcmp(AKLOC_CODE1(lc, 2).sa, sa, sizeof(AKLOC_CODE1(lc, 2).sa));
+}
+
+int airkiss_filter(const void *f, int len)
+{
+    int ret = 0;
+    unsigned char *da, *p;
+    int i;
+
+    p = (unsigned char *)f;
+    if ((len < 25) || (p[0] != 0x08))
+        return 1;
+
+    da = p + 4;
+
+    for (i = 0; i < 6; i++)
+    {
+        if (da[i] != 0xFF)
+        {
+            ret = 1;
+            break;
+        }
+    }
+
+    return ret;
+}
+
+static int _ak_recv(airkiss_context_t *c, const void *frame, uint16_t length, int nossid)
+{
+    int ret = AIRKISS_STATUS_CONTINUE;
+    akloc_context_t *lc = (akloc_context_t *)c;
+    unsigned char *f = (unsigned char *)frame;
+
+    if (frame != NULL) /* 模拟测试时可只传length */
+    {
+        if (airkiss_filter(frame, length))
+            return ret;
+        if ((lc->state != AKSTATE_WFG) && ak_sa_filter(lc, f))
+            return ret;
+    }
+
+    switch (lc->state)
+    {
+    case AKSTATE_WFG:
+    {
+        ret = ak_waitfor_guidefield(lc, f, length);
+    }
+    break;
+    case AKSTATE_WFM:
+    {
+        ret = ak_waitfor_magicfield(lc, length);
+    }
+    break;
+    case AKSTATE_WFP:
+    {
+        ret = ak_waitfor_prefixfield(lc, length);
+    }
+    break;
+    case AKSTATE_WFD:
+    {
+        ret = ak_waitfor_datafield(lc, length, nossid);
+    }
+    break;
+    case AKSTATE_CMP:
+    {
+        ret = AIRKISS_STATUS_COMPLETE;
+    }
+    break;
+    }
+
+    return ret;
+}
+
+const char *airkiss_version(void)
+{
+    return "airkiss-1.0.0-open";
+}
+
+int airkiss_init(airkiss_context_t *c, const airkiss_config_t *config)
+{
+    akloc_context_t *lc = (akloc_context_t *)c;
+
+    lc->cfg = config;
+    akloc_reset(lc);
+
+    return 0;
+}
+
+int airkiss_recv(airkiss_context_t *c, const void *frame, unsigned short length)
+{
+    return _ak_recv(c, frame, length, 0);
+}
+
+int airkiss_get_result(airkiss_context_t *c, airkiss_result_t *res)
+{
+    akloc_context_t *lc = (akloc_context_t *)c;
+
+    if (lc->state != AKSTATE_CMP)
+        return -1;
+
+    res->pwd = (char *)&lc->data[0];
+    res->pwd_length = lc->pwdlen;
+    if (lc->data[lc->pwdlen] == 0)
+    {
+        res->random = lc->random;
+    }
+    else
+    {
+        res->random = lc->data[lc->pwdlen];
+        lc->random = lc->data[lc->pwdlen];
+        lc->data[lc->pwdlen] = 0;
+    }
+
+    res->ssid_crc = lc->ssidcrc;
+    if (lc->nossid)
+    {
+        res->ssid = "";
+        res->ssid_length = 0;
+    }
+    else
+    {
+        res->ssid = (char *)&lc->data[lc->pwdlen + 1];
+        res->ssid_length = lc->prslen - lc->pwdlen - 1;
+    }
+    lc->data[lc->prslen] = 0;
+
+    return 0;
+}
+
+int airkiss_recv_nossid(airkiss_context_t *c, const void *frame, unsigned short length)
+{
+    return _ak_recv(c, frame, length, 1);
+}
+
+int airkiss_change_channel(airkiss_context_t *c)
+{
+    akloc_context_t *lc = (akloc_context_t *)c;
+
+    akloc_reset(lc);
+
+    return 0;
+}

+ 253 - 0
airkiss.h

@@ -0,0 +1,253 @@
+/*
+ * airkiss.h
+ *
+ *  Created on: 2015-1-26
+ *      Author: peterfan
+ */
+
+#ifndef AIRKISS_H_
+#define AIRKISS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 定义AIRKISS_ENABLE_CRYPT为1以启用AirKiss加密功能
+ */
+#ifndef AIRKISS_ENABLE_CRYPT
+#define AIRKISS_ENABLE_CRYPT 0
+#endif
+
+
+typedef void* (*airkiss_memset_fn) (void* ptr, int value, unsigned int num);
+typedef void* (*airkiss_memcpy_fn) (void* dst, const void* src, unsigned int num);
+typedef int (*airkiss_memcmp_fn) (const void* ptr1, const void* ptr2, unsigned int num);
+typedef int (*airkiss_printf_fn) (const char* format, ...);
+
+
+/*
+ * 对AirKiss库进行配置,目前仅定义了一些回调函数
+ */
+typedef struct
+{
+	/*
+	 * 为尽量减少库文件体积,如下c标准库函数需要上层使用者提供
+	 * 其中printf可以为NULL
+	 */
+	airkiss_memset_fn memset;
+	airkiss_memcpy_fn memcpy;
+	airkiss_memcmp_fn memcmp;
+	airkiss_printf_fn printf;
+
+} airkiss_config_t;
+
+
+
+/*
+ * AirKiss API工作需要的结构体,必须为全局变量或者通过malloc动态分配
+ */
+typedef struct
+{
+	int dummyap[26];
+	int dummy[32];
+} airkiss_context_t;
+
+
+
+/*
+ * AirKiss解码成功后的结果
+ */
+typedef struct
+{
+	char* pwd;						/* wifi密码,以'\0'结尾 */
+	char* ssid;						/* wifi ssid,以'\0'结尾 */
+	unsigned char pwd_length;		/* wifi密码长度 */
+	unsigned char ssid_length;		/* wifi ssid长度 */
+	unsigned char random;			/* 随机值,根据AirKiss协议,当wifi连接成功后,需要通过udp向10000端口广播这个随机值,这样AirKiss发送端(微信客户端或者AirKissDebugger)就能知道AirKiss已配置成功 */
+        unsigned char ssid_crc;			/* SSID crc8 */
+} airkiss_result_t;
+
+
+
+/*
+ * airkiss_recv()正常情况下的返回值
+ */
+typedef enum
+{
+	/* 解码正常,无需特殊处理,继续调用airkiss_recv()直到解码成功 */
+	AIRKISS_STATUS_CONTINUE = 0,
+
+	/* wifi信道已经锁定,上层应该立即停止切换信道 */
+	AIRKISS_STATUS_CHANNEL_LOCKED = 1,
+
+	/* 解码成功,可以调用airkiss_get_result()取得结果 */
+	AIRKISS_STATUS_COMPLETE = 2
+
+} airkiss_status_t;
+
+
+
+#if AIRKISS_ENABLE_CRYPT
+
+/*
+ * 设置解密key,最长可以为128bit,若传入的key不足128bit,则默认用0填充
+ *
+ * 返回值
+ * 		< 0:出错,通常是参数错误
+ * 		  0:成功
+ */
+int airkiss_set_key(airkiss_context_t* context, const unsigned char* key, unsigned int length);
+
+#endif
+
+
+
+/*
+ * 获取AirKiss库版本信息
+ */
+const char* airkiss_version(void);
+
+
+
+/*
+ * 初始化AirKiss库,如要复用context,可以多次调用
+ *
+ * 返回值
+ * 		< 0:出错,通常是参数错误
+ * 		  0:成功
+ */
+int airkiss_init(airkiss_context_t* context, const airkiss_config_t* config);
+
+
+
+/*
+ * 开启WiFi Promiscuous Mode后,将收到的包传给airkiss_recv以进行解码
+ *
+ * 参数说明
+ * 		frame:802.11 frame mac header(must contain at least first 24 bytes)
+ * 		length:total frame length
+ *
+ * 返回值
+ * 		 < 0:出错,通常是参数错误
+ * 		>= 0:成功,请参考airkiss_status_t
+ */
+int airkiss_recv(airkiss_context_t* context, const void* frame, unsigned short length);
+
+
+
+/*
+ * 当airkiss_recv()返回AIRKISS_STATUS_COMPLETE后,调用此函数来获取AirKiss解码结果
+ *
+ * 返回值
+ * 		< 0:出错,解码状态还不是AIRKISS_STATUS_COMPLETE
+ * 		  0:成功
+ */
+int airkiss_get_result(airkiss_context_t* context, airkiss_result_t* result);
+
+
+/*
+ * 上层切换信道以后,可以调用一下本接口清缓存,降低锁定错信道的概率,注意调用的逻辑是在airkiss_init之后
+ *
+ * 返回值
+ * 		< 0:出错,通常是参数错误
+ * 		  0:成功
+ */
+int airkiss_change_channel(airkiss_context_t* context);
+
+/*
+ *
+ * 以上是实现智能配置网络的相关API,以下是微信内网发现相关API
+ *
+ */
+
+/*
+ * airkiss_lan_recv()的返回值
+ */
+typedef enum
+{
+	/* 提供的数据缓冲区长度不足 */
+	AIRKISS_LAN_ERR_OVERFLOW = -5,
+
+	/* 当前版本不支持的指令类型 */
+	AIRKISS_LAN_ERR_CMD = -4,
+
+	/* 打包数据出错 */
+	AIRKISS_LAN_ERR_PAKE = -3,
+
+	/* 函数传递参数出错 */
+	AIRKISS_LAN_ERR_PARA = -2,
+
+	/* 报文数据错误 */
+	AIRKISS_LAN_ERR_PKG = -1,
+
+	/* 报文格式正确,但是不需要设备处理的数据包 */
+	AIRKISS_LAN_CONTINUE = 0,
+
+	/* 接收到发现设备请求数据包 */
+	AIRKISS_LAN_SSDP_REQ = 1,
+
+	/* 数据包打包完成 */
+	AIRKISS_LAN_PAKE_READY = 2
+
+
+} airkiss_lan_ret_t;
+
+
+typedef enum
+{
+	AIRKISS_LAN_SSDP_REQ_CMD = 0x1,
+	AIRKISS_LAN_SSDP_RESP_CMD = 0x1001,
+	AIRKISS_LAN_SSDP_NOTIFY_CMD = 0x1002
+} airkiss_lan_cmdid_t;
+
+/*
+ * 设备进入内网发现模式后,将收到的包传给airkiss_lan_recv以进行解析
+ *
+ * 参数说明
+ * 		body:802.11 frame mac header(must contain at least first 24 bytes)
+ * 		length:total frame length
+ * 		config:AirKiss回调函数
+ *
+ * 返回值
+ * 		 < 0:出错,请参考airkiss_lan_ret_t,通常是报文数据出错
+ * 		>= 0:成功,请参考airkiss_lan_ret_t
+ */
+int airkiss_lan_recv(const void* body, unsigned short length, const airkiss_config_t* config);
+
+/*
+ * 设备要发送内网协议包时,调用本接口完成数据包打包
+ *
+ * 参数说明
+ * 		body:802.11 frame mac header(must contain at least first 24 bytes)
+ * 		length:total frame length
+ * 		config:AirKiss回调函数
+ *
+ * 返回值
+ * 		 < 0:出错,请参考airkiss_lan_ret_t,通常是报文数据出错
+ * 		>= 0:成功,请参考airkiss_lan_ret_t
+ */
+int airkiss_lan_pack(airkiss_lan_cmdid_t ak_lan_cmdid, void* appid, void* deviceid, void* _datain, unsigned short inlength, void* _dataout, unsigned short* outlength, const airkiss_config_t* config);
+
+/*
+ * 过滤部分不符合要求的帧
+ * 
+ * 参数说明
+ * 		frame:802.11 frame mac header(must contain at least first 24 bytes)
+ * 		length:total frame length
+ *
+ * 返回值
+ * 		  0:有效
+ * 		  1:无效
+*/
+int airkiss_filter(const void *frame, int length);
+
+unsigned char airkiss_crc8(unsigned char *message, unsigned char len);
+
+int airkiss_recv_nossid(airkiss_context_t* context, const void* frame, unsigned short length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AIRKISS_H_ */

+ 205 - 0
doc/cap1.txt

@@ -0,0 +1,205 @@
+08620002FFFFFFFFFFFFFC2FEF51363D7C03AB7353129050:386
+08620002FFFFFFFFFFFFFC2FEF51363C7C03AB735312A050:386
+08620002FFFFFFFFFFFFFC2FEF51363D7C03AB735312B050:398
+08420002FFFFFFFFFFFFFC2FEF51363C7C03AB735312C050:398
+08620002FFFFFFFFFFFFFC2FEF51363D7C03AB735312D050:80
+08420002FFFFFFFFFFFFFC2FEF51363C7C03AB735312E050:80
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF8051:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF9051:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA051:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB051:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC051:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD051:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE051:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF051:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF0052:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF1052:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF3052:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF5052:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF9052:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE052:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF2053:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF6053:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF8053:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF9053:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA053:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB053:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC053:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD053:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE053:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF053:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF0054:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF2054:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF3054:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF6054:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA054:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE054:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF0055:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF4055:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF8055:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF9055:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA055:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB055:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC055:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD055:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE055:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF055:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF0056:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF2056:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF5056:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF6056:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF8056:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD056:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF2057:83
+08420002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF7057:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFF057:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF0058:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1058:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF2058:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF3058:82
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF4058:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5058:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF6058:83
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF7058:84
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF8058:84
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF9058:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA058:81
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFC058:82
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1059:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5059:83
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA059:81
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF205A:88
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF305A:88
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF405A:108
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF505A:108
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF605A:118
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF705A:118
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805A:134
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF905A:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA05A:88
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB05A:88
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC05A:108
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE05A:118
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF105B:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF405B:108
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805B:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC05B:108
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF05B:118
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF205C:118
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF305C:118
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF405C:134
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF505C:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF605C:88
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF705C:88
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805C:108
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF905C:108
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA05C:118
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB05C:118
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC05C:134
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD05C:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE05C:88
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF005D:108
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF205D:118
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF505D:134
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805D:163
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB05D:190
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD05D:194
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF05D:144
+08620002FFFFFFFFFFFFFC2FEF51513DFC2FEF9276CE605F:80
+08420002FFFFFFFFFFFFFC2FEF51513CFC2FEF9276CE705F:80
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF205E:163
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF305E:163
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF405E:190
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF505E:190
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF605E:194
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF705E:194
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805E:144
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF905E:144
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA05E:163
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB05E:163
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC05E:190
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD05E:190
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE05E:194
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF05E:194
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF005F:144
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF105F:144
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF205F:163
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF305F:163
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF405F:190
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF505F:190
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF605F:194
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF705F:194
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF805F:144
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF905F:144
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFA05F:163
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFB05F:163
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFC05F:190
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFD05F:190
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFE05F:194
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFF05F:194
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF0060:144
+08420002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF1060:144
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF3060:449
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF4060:449
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5060:455
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF6060:455
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF7060:437
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF8060:437
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF9060:423
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA060:423
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFB060:270
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFC060:270
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFD060:209
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFE060:209
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFF060:403
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF0061:403
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1061:404
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF2061:404
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF3061:408
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF4061:408
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5061:414
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF6061:414
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF7061:277
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF8061:277
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF9061:210
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA061:210
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFB061:431
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFC061:431
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFD061:385
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFE061:385
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFF061:384
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF0062:384
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1062:387
+08420002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF2062:387
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFF062:403
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF0063:403
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1063:404
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF2063:404
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF3063:408
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF4063:408
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5063:414
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF6063:414
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF7063:277
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF8063:277
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF9063:210
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA063:210
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFB063:431
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFC063:431
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFD063:385
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFE063:385
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFF063:384
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF0064:384
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF1064:387
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF2064:387
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF3064:287
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF4064:287
+08420002FFFFFFFFFFFFFC2FEF51513DFC2FEF9276CE6060:80
+08420002FFFFFFFFFFFFFC2FEF51513CFC2FEF9276CE7060:80
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF5064:208
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF7064:449
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CF8064:449
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CF9064:455
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFA064:455
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFB064:437
+08620002FFFFFFFFFFFFFC2FEF51363C4C49E31A12CFC064:437
+08620002FFFFFFFFFFFFFC2FEF51363D4C49E31A12CFD064:423

+ 55 - 0
doc/test-report.md

@@ -0,0 +1,55 @@
+手机: redmi note4x
+wifisoc: w600
+
+发送工具: airkissdebugger.apk
+
+输出airkiss-1.0.0-open后开始发送
+
+```
+2019-05-16
+
+/*  路由型号: 未知  */
+[12:43:22.529] airkiss: airkiss-1.0.0-open
+[12:43:26.292] airkiss locked chn 11
+[12:43:36.229] pwd 12345678901234567890 ssid CDHN_103
+
+[12:46:44.361] airkiss: airkiss-1.0.0-open
+[12:46:48.970] airkiss locked chn 11
+[12:47:05.864] pwd 12345678901234567890 ssid CDHN_103
+
+[12:49:22.867] airkiss: airkiss-1.0.0-open
+[12:49:47.162] airkiss locked chn 11
+[12:50:04.192] pwd 12345678901234567890 ssid
+
+[12:52:51.499] airkiss: airkiss-1.0.0-open 
+[12:52:55.159] airkiss locked chn 11
+[12:53:05.038] pwd 12345678901234567890 ssid CDHN_103
+
+[12:56:33.587] airkiss: airkiss-1.0.0-open
+[12:56:39.237] airkiss locked chn 11
+[12:56:50.772] pwd qwertyuiopasd ssid CDHN_103
+
+/* 路由型号: 未知 */
+[12:59:35.224] airkiss: airkiss-1.0.0-open
+[12:59:53.060] airkiss locked chn 6
+[12:59:56.243] pwd qertuiopadghh ssid CDHN_Test
+
+/* 路由型号: 未知 */
+[13:03:41.357] airkiss: airkiss-1.0.0-open
+[13:03:59.933] airkiss locked chn 9
+[13:04:07.905] airkiss locked chn 9 
+[13:05:11.054] airkiss locked chn 9
+[13:05:18.451] airkiss locked chn 9 
+[13:05:24.069] pwd wrtuiiosfhklkggj ssid FAST_157
+
+[13:09:17.259] airkiss: airkiss-1.0.0-open 
+[13:09:58.990] airkiss locked chn 9
+[13:10:35.238] airkiss locked chn 9
+//失败后改变密码内容
+[13:11:03.706] airkiss locked chn 9
+[13:11:07.020] pwd 1234567890 ssid FAST_157
+
+[13:25:32.455] airkiss: airkiss-1.0.0-open 
+[13:25:40.562] airkiss locked chn 9 
+[13:25:43.062] pwd 1234567890qwe ssid FAST_157
+```

+ 40 - 0
osdep/qt/airkiss.pro

@@ -0,0 +1,40 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2019-05-09T13:47:22
+#
+#-------------------------------------------------
+
+QT       += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets network
+
+DEFINES +=AIRKISS_80211HDR_NONE
+TARGET = airkiss
+TEMPLATE = app
+DESTDIR = ../../doc
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+
+SOURCES += \
+        main.cpp \
+        mainwindow.cpp \
+    akWorker.cpp \
+    ../../airkiss.c
+
+HEADERS += \
+        mainwindow.h \
+    akWorker.h
+    ../../airkiss.h
+
+FORMS += \
+        mainwindow.ui

+ 197 - 0
osdep/qt/akWorker.cpp

@@ -0,0 +1,197 @@
+#include "akWorker.h"
+
+#include <QUdpSocket>
+#include <QNetworkInterface>
+#include <QHostInfo>
+
+#include "../../airkiss.h"
+
+#include <QFile>
+#include <QTextStream>
+#include <stdio.h>
+
+static int msgprint(const char *format, ...)
+{
+    char buf[80];
+    int i;
+    va_list vlist;
+
+    va_start(vlist, format);
+    i = vsprintf(buf, format, vlist);
+    va_end(vlist);
+
+    qDebug(buf);
+
+    return i;
+}
+
+akWorker::akWorker()
+{
+    isrun = true;
+    lenOut = false;
+    cmpExit = true;
+}
+
+akWorker::~akWorker()
+{
+    isrun = false;
+    wait();
+}
+
+void akWorker::start(QString file, bool nossid, short port)
+{
+    _file = file;
+    _nossid = nossid;
+    _port = port;
+    QThread::start();
+}
+
+int akWorker::getframe(QString &str, QByteArray &fm)
+{
+    QStringList list;
+    int len;
+
+    fm.clear();
+    list = str.split(":");
+    if (list.count() == 1)
+    {
+        len = list.at(0).toInt();
+    }
+    else
+    {
+        QByteArray tmp;
+
+        tmp = list.at(0).toStdString().c_str();
+        fm = fm.fromHex(tmp);
+        len = list.at(1).toInt();
+    }
+
+    return len;
+}
+
+void akWorker::fromfile()
+{
+    QFile fd;
+    airkiss_context_t ac;
+    airkiss_config_t acfg = {0,0,0,0};
+    int status;
+    int len;
+    QString data;
+    QTextStream in;
+    QByteArray fm;
+
+    fd.setFileName(_file);
+    if (!fd.open(QIODevice::ReadOnly))
+    {
+        emit showMsg("open file fail");
+        return;
+    }
+
+    in.setDevice(&fd);
+
+    acfg.printf = msgprint;
+    airkiss_init(&ac, &acfg);
+
+    while (isrun)
+    {
+        char *pf = NULL;
+
+        data = in.readLine();
+        if (data.size() == 0)
+            break;
+
+        len = getframe(data, fm);
+        if (len == 0)
+            break;
+        if (fm.size() > 0)
+            pf = fm.data();
+
+        status = airkiss_recv(&ac, pf, len);
+        if (status == AIRKISS_STATUS_COMPLETE)
+        {
+            airkiss_result_t res;
+            QString str;
+
+            airkiss_get_result(&ac, &res);
+            str = str.sprintf("ssid: %s, pwd: %s\n", res.ssid, res.pwd);
+            emit showMsg(str);
+            break;
+        }
+        else if (status == AIRKISS_STATUS_CHANNEL_LOCKED)
+        {
+            emit showMsg("锁定通道");
+        }
+
+        msleep(1);
+    }
+}
+
+void akWorker::fromudp_bc()
+{
+    QUdpSocket *ser;
+    char buf[1024];
+    airkiss_context_t ac;
+    int status = -1;
+
+    ser = new QUdpSocket;
+    if (!ser->bind(QHostAddress::AnyIPv4, _port, QUdpSocket::ShareAddress))
+    {
+        emit showMsg("bind fail");
+        goto _out;
+    }
+
+    airkiss_init(&ac, NULL);
+
+    while (isrun)
+    {
+        int len;
+
+        if (ser->hasPendingDatagrams())
+        {
+            len = ser->pendingDatagramSize();
+            ser->readDatagram(buf, len);
+            if (lenOut)
+            {
+                QString s;
+
+                s = s.sprintf("%X", len);
+                emit showMsg(s);
+            }
+
+            if (_nossid)
+                status = airkiss_recv_nossid(&ac, NULL, len);
+            else
+                status = airkiss_recv(&ac, NULL, len);
+
+            if (status == AIRKISS_STATUS_COMPLETE)
+            {
+                airkiss_result_t res;
+                QString str;
+
+                airkiss_get_result(&ac, &res);
+                if (_nossid)
+                    str = str.sprintf("ssidcrc: %X, pwd: %s\n", res.ssid_crc, res.pwd);
+                else
+                    str = str.sprintf("ssid: %s, pwd: %s\n", res.ssid, res.pwd);
+                emit showMsg(str);
+
+                airkiss_change_channel(&ac);
+                if (cmpExit)
+                    break;
+            }
+        }
+    }
+
+_out:
+    delete ser;
+}
+
+void akWorker::run()
+{
+    if (_file.isEmpty())
+        fromudp_bc();
+    else
+        fromfile();
+
+    emit showMsg("退出解析");
+}

+ 41 - 0
osdep/qt/akWorker.h

@@ -0,0 +1,41 @@
+#ifndef AKWORKER_H
+#define AKWORKER_H
+
+#include <QObject>
+#include <QThread>
+
+class QFile;
+
+class akWorker : public QThread
+{
+    Q_OBJECT
+
+public:
+    akWorker();
+    ~akWorker();
+
+    void start(QString file = "", bool nossid = true, short port = 10000);
+
+signals:
+    showMsg(QString m);
+
+private:
+    void run();
+
+    void fromfile();
+    void fromudp_bc();
+
+    int getframe(QString &str, QByteArray &fm);
+
+public:
+    bool lenOut;
+    bool cmpExit;
+
+private:
+    bool isrun;
+    QString _file;
+    bool _nossid;
+    short _port;
+};
+
+#endif // AKWORKER_H

+ 11 - 0
osdep/qt/main.cpp

@@ -0,0 +1,11 @@
+#include "mainwindow.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+    MainWindow w;
+    w.show();
+
+    return a.exec();
+}

+ 118 - 0
osdep/qt/mainwindow.cpp

@@ -0,0 +1,118 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include "akWorker.h"
+#include "../../airkiss.h"
+
+#include <QFileDialog>
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::MainWindow)
+{
+    ui->setupUi(this);
+
+    wk = 0;
+}
+
+MainWindow::~MainWindow()
+{
+    delete ui;
+}
+
+void MainWindow::showMsg(QString m)
+{
+    ui->peres->appendPlainText(m);
+}
+
+void MainWindow::workerExit()
+{
+    if (wk)
+    {
+        QString str = "启动";
+
+        ui->pbparese->setText(str);
+        ui->pbst->setText(str);
+
+        delete wk;
+        wk = NULL;
+    }
+}
+
+void MainWindow::on_pbst_clicked()
+{
+    if (wk)
+    {
+        delete wk;
+        wk = 0;
+        ui->pbst->setText(QString("启动"));
+    }
+    else
+    {
+        wk = new akWorker;
+
+        connect(wk, SIGNAL(showMsg(QString)),
+                this, SLOT(showMsg(QString)));
+        connect(wk, SIGNAL(finished()),
+                this, SLOT(workerExit()));
+
+        ui->peres->clear();
+        wk->lenOut = ui->cblen->isChecked();
+        wk->cmpExit = ui->cbcmpe->isChecked();
+        wk->start("", ui->nossid->isChecked(), ui->sbport->value());
+
+        ui->pbst->setText(QString("停止"));
+    }
+}
+
+void MainWindow::on_pbparese_clicked()
+{
+    QString file;
+
+    file = ui->lepath->currentText();
+    if (file.isEmpty())
+        return;
+
+    if (wk)
+    {
+        delete wk;
+        wk = 0;
+        ui->pbparese->setText(QString("启动"));
+    }
+    else
+    {
+        wk = new akWorker;
+
+        connect(wk, SIGNAL(showMsg(QString)),
+                this, SLOT(showMsg(QString)));
+        connect(wk, SIGNAL(finished()),
+                this, SLOT(workerExit()));
+
+        wk->start(file);
+
+        ui->pbparese->setText(QString("停止"));
+    }
+}
+
+void MainWindow::on_pbpath_clicked()
+{
+    QString dir;
+
+    dir = QFileDialog::getOpenFileName(this, "选择文件", "./");
+    if (!dir.isEmpty())
+    {
+       ui->lepath->setCurrentText(dir);
+    }
+}
+
+void MainWindow::on_cblen_toggled(bool checked)
+{
+    if (wk)
+        wk->lenOut = checked;
+}
+
+void MainWindow::on_cbcmpe_toggled(bool checked)
+{
+    if (wk)
+        wk->cmpExit = checked;
+}

+ 40 - 0
osdep/qt/mainwindow.h

@@ -0,0 +1,40 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+namespace Ui {
+class MainWindow;
+}
+
+class akWorker;
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+
+private slots:
+    void showMsg(QString m);
+    void workerExit();
+
+private slots:
+    void on_pbst_clicked();
+
+    void on_pbparese_clicked();
+
+    void on_pbpath_clicked();
+
+    void on_cblen_toggled(bool checked);
+
+    void on_cbcmpe_toggled(bool checked);
+
+private:
+    Ui::MainWindow *ui;
+    akWorker *wk;
+};
+
+#endif // MAINWINDOW_H

+ 234 - 0
osdep/qt/mainwindow.ui

@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>826</width>
+    <height>511</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>airkiss解析</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <widget class="QGroupBox" name="groupBox">
+    <property name="geometry">
+     <rect>
+      <x>420</x>
+      <y>320</y>
+      <width>311</width>
+      <height>111</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>加载文件</string>
+    </property>
+    <widget class="QPushButton" name="pbpath">
+     <property name="geometry">
+      <rect>
+       <x>200</x>
+       <y>30</y>
+       <width>61</width>
+       <height>24</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>...</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="pbparese">
+     <property name="geometry">
+      <rect>
+       <x>60</x>
+       <y>70</y>
+       <width>89</width>
+       <height>24</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>启动</string>
+     </property>
+    </widget>
+    <widget class="QComboBox" name="lepath">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>30</y>
+       <width>151</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="editable">
+      <bool>false</bool>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <item>
+      <property name="text">
+       <string>cap1.txt</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>cap2.txt</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>cap3.txt</string>
+      </property>
+     </item>
+    </widget>
+   </widget>
+   <widget class="QGroupBox" name="groupBox_2">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>300</y>
+      <width>301</width>
+      <height>151</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>UDP</string>
+    </property>
+    <widget class="QPushButton" name="pbst">
+     <property name="geometry">
+      <rect>
+       <x>140</x>
+       <y>70</y>
+       <width>111</width>
+       <height>31</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>启动</string>
+     </property>
+    </widget>
+    <widget class="QCheckBox" name="nossid">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>70</y>
+       <width>111</width>
+       <height>31</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>不接收SSID</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QCheckBox" name="cblen">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>30</y>
+       <width>101</width>
+       <height>18</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>输出Length</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label">
+     <property name="geometry">
+      <rect>
+       <x>120</x>
+       <y>30</y>
+       <width>41</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>端口</string>
+     </property>
+    </widget>
+    <widget class="QSpinBox" name="sbport">
+     <property name="geometry">
+      <rect>
+       <x>160</x>
+       <y>30</y>
+       <width>71</width>
+       <height>22</height>
+      </rect>
+     </property>
+     <property name="maximum">
+      <number>65535</number>
+     </property>
+     <property name="value">
+      <number>10000</number>
+     </property>
+    </widget>
+    <widget class="QCheckBox" name="cbcmpe">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>120</y>
+       <width>73</width>
+       <height>18</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>完成退出</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QGroupBox" name="groupBox_3">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>20</y>
+      <width>401</width>
+      <height>251</height>
+     </rect>
+    </property>
+    <property name="title">
+     <string>结果</string>
+    </property>
+    <widget class="QPlainTextEdit" name="peres">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>20</y>
+       <width>361</width>
+       <height>211</height>
+      </rect>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>826</width>
+     <height>22</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QToolBar" name="mainToolBar">
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 501 - 0
osdep/rtthread/airkiss_demo.c

@@ -0,0 +1,501 @@
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <sys/socket.h>
+
+#include "../../airkiss.h"
+
+#include <stdlib.h>
+
+#define AKDEMO_BUFSZ 16
+
+static rt_list_t _bflist;
+static int _mbuf[AKDEMO_BUFSZ];
+static struct rt_mailbox _bfready;
+static int _akrun = 0;
+
+typedef struct
+{
+    rt_list_t list;
+    short fmlen;
+    char f[24];
+} akbuf_t;
+
+typedef struct
+{
+    char n;
+    unsigned char tm;
+    unsigned short cnt;
+} akchn_t;
+
+static akbuf_t *ak_bufinit(int cnt)
+{
+    int i;
+    akbuf_t *buf, *p;
+
+    rt_list_init(&_bflist);
+
+    buf = (akbuf_t *)rt_calloc(cnt, sizeof(akbuf_t));
+    p = buf;
+    for (i = 0; (i < cnt) && buf; i++)
+    {
+        p->fmlen = 0;
+        rt_list_init(&p->list);
+        rt_list_insert_after(&_bflist, &p->list);
+        p++;
+    }
+
+    return buf;
+}
+
+static void prom_callback(struct rt_wlan_device *device, void *d, int s)
+{
+    akbuf_t *buf;
+
+    if (airkiss_filter(d, s))
+        return;
+
+    if (rt_list_isempty(&_bflist))
+    {
+        rt_kprintf("ak no buf\n");
+        return;
+    }
+
+    buf = rt_list_first_entry(&_bflist, akbuf_t, list);
+    rt_list_remove(&buf->list);
+
+    rt_memcpy(buf->f, d, 24);
+    buf->fmlen = s;
+    if (rt_mb_send(&_bfready, (rt_ubase_t)buf) != 0)
+    {
+        rt_list_insert_before(&_bflist, &buf->list);
+        rt_kprintf("ak send fail\n");
+    }
+}
+
+static int ak_recv(airkiss_context_t *ac, int ms, int *rcnt)
+{
+    akbuf_t *buf = 0;
+    int status = AIRKISS_STATUS_CONTINUE;
+    rt_tick_t st, max;
+
+    st = rt_tick_get();
+
+    while (1)
+    {
+        if (rt_mb_recv(&_bfready, (rt_ubase_t *)&buf, rt_tick_from_millisecond(10)) == 0)
+        {
+            status = airkiss_recv(ac, buf->f, buf->fmlen);
+            rt_list_insert_after(&_bflist, &buf->list);
+            if (status != AIRKISS_STATUS_CONTINUE)
+                break;
+
+            (*rcnt)++;
+            /* 收到数据奖励时间 */
+            if (ms < 100)
+                ms += 8;
+        }
+
+        /* 超时后进行时间惩罚 */
+        if (ms > 40)
+            ms -= 2;
+        max = rt_tick_from_millisecond(ms);
+        if ((rt_tick_get() - st) > max)
+            break;
+    }
+
+    return status;
+}
+
+static int ak_recv_chn(struct rt_wlan_device *dev,
+                       airkiss_context_t *ac, akchn_t *chn)
+{
+    int status;
+    int t;
+    int rcnt = 0;
+
+    rt_wlan_dev_set_channel(dev, chn->n);
+    airkiss_change_channel(ac);
+
+    status = ak_recv(ac, 50 + chn->tm, &rcnt);
+    if (status == AIRKISS_STATUS_CHANNEL_LOCKED)
+    {
+        rt_kprintf("airkiss locked chn %d\n", chn->n);
+        status = ak_recv(ac, 1000 * 30, &rcnt);
+    }
+
+    chn->cnt += rcnt;
+    t = chn->cnt * 8;
+    if (t)
+    {
+        if (t > 50)
+            t = 50;
+        chn->tm = t;
+        //rt_kprintf("tm %d on chn %d\n", chn->tm, chn->n);
+    }
+
+    return status;
+}
+
+static void akchn_init(akchn_t *chn, int n)
+{
+    int i;
+
+    for (i = 0; i < n; i++)
+    {
+        chn[i].n = i + 1;
+        chn[i].cnt = 0;
+    }
+}
+
+static int ak_wifi_connetct(char *ssid, char *passwd)
+{
+    int result = RT_EOK;
+    rt_uint8_t time_cnt = 0;
+
+#define NET_READY_TIME_OUT (rt_tick_from_millisecond(8 * 1000))
+
+    result = rt_wlan_connect(ssid, passwd);
+    if (result != RT_EOK)
+    {
+        rt_kprintf("\nconnect ssid %s error:%d!\n", ssid, result);
+        return result;
+    };
+
+    do
+    {
+        rt_thread_mdelay(1000);
+        time_cnt++;
+        if (rt_wlan_is_ready())
+        {
+            break;
+        }
+    } while (time_cnt <= (NET_READY_TIME_OUT / 1000));
+
+    if (time_cnt <= (NET_READY_TIME_OUT / 1000))
+    {
+        rt_kprintf("networking ready!\n");
+    }
+    else
+    {
+        rt_kprintf("wait ip got timeout!\n");
+        result = -1;
+    }
+
+    return result;
+}
+
+static void airkiss_send_notification(uint8_t random)
+{
+    int sock = -1;
+    int udpbufsize = 2;
+    struct sockaddr_in UDPBCAddr, UDPBCServerAddr;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0)
+    {
+        rt_kprintf("notify create socket error!\n");
+        goto _exit;
+    }
+
+    UDPBCAddr.sin_family = AF_INET;
+    UDPBCAddr.sin_port = htons(10000);
+    UDPBCAddr.sin_addr.s_addr = htonl(0xffffffff);
+    rt_memset(&(UDPBCAddr.sin_zero), 0, sizeof(UDPBCAddr.sin_zero));
+
+    UDPBCServerAddr.sin_family = AF_INET;
+    UDPBCServerAddr.sin_port = htons(10000);
+    UDPBCServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    rt_memset(&(UDPBCServerAddr.sin_zero), 0, sizeof(UDPBCServerAddr.sin_zero));
+
+    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &udpbufsize, sizeof(int)) != 0)
+    {
+        rt_kprintf("notify socket setsockopt error\n");
+        goto _exit;
+    }
+
+    if (bind(sock, (struct sockaddr *)&UDPBCServerAddr, sizeof(UDPBCServerAddr)) != 0)
+    {
+        rt_kprintf("notify socket bind error\n");
+        goto _exit;
+    }
+
+    for (int i = 0; i <= 20; i++)
+    {
+        int ret = sendto(sock, (char *)&random, 1, 0, (struct sockaddr *)&UDPBCAddr, sizeof(UDPBCAddr));
+        rt_thread_mdelay(10);
+    }
+
+_exit:
+    if (sock >= 0)
+    {
+        closesocket(sock);
+    }
+}
+
+static void airkiss_thread(void *p)
+{
+    airkiss_context_t ac;
+    struct rt_wlan_device *dev;
+    akbuf_t *pbuf;
+    int n;
+    akchn_t chns[12];
+    int round = 80;
+    airkiss_config_t acfg =
+        {
+            (airkiss_memset_fn)&rt_memset,
+            (airkiss_memcpy_fn)&rt_memcpy,
+            (airkiss_memcmp_fn)&rt_memcmp,
+            0};
+
+    rt_kprintf("airkiss thread start\n");
+    if ((pbuf = ak_bufinit(AKDEMO_BUFSZ)) == RT_NULL)
+    {
+        rt_kprintf("akdemo init buf err\n");
+        return;
+    }
+
+    airkiss_init(&ac, &acfg);
+
+    rt_mb_init(&_bfready, "ak", _mbuf, AKDEMO_BUFSZ, 0);
+    rt_wlan_disconnect();
+    dev = (struct rt_wlan_device *)rt_device_find(RT_WLAN_DEVICE_STA_NAME);
+    rt_wlan_dev_set_promisc_callback(dev, prom_callback);
+
+    rt_wlan_dev_enter_promisc(dev);
+
+    akchn_init(chns, 12);
+
+    rt_kprintf("%s\n", airkiss_version());
+
+    while (round-- > 0)
+    {
+        //rt_kprintf("ak round\n");
+        for (n = 0; n < 12; n++)
+        {
+            if (ak_recv_chn(dev, &ac, &chns[n]) == AIRKISS_STATUS_COMPLETE)
+            {
+                airkiss_result_t res;
+
+                rt_wlan_dev_exit_promisc(dev);
+
+                airkiss_get_result(&ac, &res);
+                rt_kprintf("pwd %s ssid %s\n", res.pwd, res.ssid);
+
+                if (ak_wifi_connetct(res.ssid, res.pwd) == 0)
+                {
+                    airkiss_send_notification(res.random);
+                }
+
+                goto _out;
+            }
+        }
+    }
+
+    rt_wlan_dev_exit_promisc(dev);
+
+_out:
+    rt_wlan_dev_set_promisc_callback(dev, RT_NULL);
+    rt_mb_detach(&_bfready);
+    rt_free(pbuf);
+    _akrun = 0;
+
+    rt_kprintf("airkiss exit\n");
+}
+
+int airkiss_demo_start(void)
+{
+    rt_thread_t tid;
+    int ret = -1;
+
+    if (_akrun)
+        return ret;
+
+    tid = rt_thread_create("airkiss",
+                           airkiss_thread,
+                           0,
+                           2048,
+                           22,
+                           20);
+
+    if (tid)
+    {
+        ret = rt_thread_startup(tid);
+    }
+    _akrun = (ret == 0);
+
+    return ret;
+}
+
+#ifdef RT_USING_FINSH
+/* 用于测试 */
+
+typedef struct
+{
+    char isrun;
+    char schn;
+    char echn;
+    char omode;
+} akcap_t;
+
+static void showakbuf(akbuf_t *buf, int mode)
+{
+    char str[58];
+    int i, pos = 0;
+
+    if (mode < 24 && mode >= 0)
+    {
+        for (i = mode; i < 24; i++)
+        {
+            rt_sprintf(str + pos, "%02X", buf->f[i]);
+            pos += 2;
+        }
+        rt_sprintf(str + pos, ":%d\n", buf->fmlen);
+
+        rt_kprintf(str);
+    }
+}
+
+static int recv_no_change(airkiss_context_t *ac, int omode)
+{
+    akbuf_t *buf = 0;
+    int status;
+
+    if (rt_mb_recv(&_bfready, (rt_ubase_t *)&buf, rt_tick_from_millisecond(10)) == 0)
+    {
+        status = airkiss_recv(ac, buf->f, buf->fmlen);
+        showakbuf(buf, omode);
+        rt_list_insert_after(&_bflist, &buf->list);
+    }
+
+    return (status == AIRKISS_STATUS_COMPLETE);
+}
+
+static void airkiss_thread_cap(void *p)
+{
+    akcap_t *cap = (akcap_t *)p;
+    airkiss_context_t ac;
+    struct rt_wlan_device *dev;
+    akbuf_t *pbuf;
+    akchn_t chns[12];
+
+    if ((pbuf = ak_bufinit(AKDEMO_BUFSZ)) == RT_NULL)
+    {
+        rt_kprintf("akdemo init buf err\n");
+        return;
+    }
+
+    airkiss_init(&ac, RT_NULL);
+
+    rt_mb_init(&_bfready, "ak", _mbuf, AKDEMO_BUFSZ, 0);
+    rt_wlan_disconnect();
+    dev = (struct rt_wlan_device *)rt_device_find(RT_WLAN_DEVICE_STA_NAME);
+    rt_wlan_dev_set_promisc_callback(dev, prom_callback);
+
+    rt_wlan_dev_enter_promisc(dev);
+
+    akchn_init(chns, 12);
+
+    rt_kprintf("airkiss cap start\n");
+
+    if (cap->schn == cap->echn)
+    {
+        rt_wlan_dev_set_channel(dev, cap->schn);
+
+        while (cap->isrun)
+        {
+            if (recv_no_change(&ac, cap->omode))
+            {
+                airkiss_result_t res;
+
+                rt_wlan_dev_exit_promisc(dev);
+
+                airkiss_get_result(&ac, &res);
+                rt_kprintf("pwd %s ssid %s\n", res.pwd, res.ssid);
+                break;
+            }
+        }
+    }
+    else
+    {
+    }
+
+    rt_wlan_dev_exit_promisc(dev);
+    rt_wlan_dev_set_promisc_callback(dev, RT_NULL);
+    rt_mb_detach(&_bfready);
+    rt_free(pbuf);
+    _akrun = 0;
+
+    rt_kprintf("airkiss exit\n");
+}
+
+static int airkiss_cap_start(akcap_t *cap)
+{
+    rt_thread_t tid;
+    int ret = -1;
+
+    if (_akrun)
+        return ret;
+
+    tid = rt_thread_create("airkiss",
+                           airkiss_thread_cap,
+                           cap,
+                           2048,
+                           22,
+                           20);
+
+    if (tid)
+    {
+        ret = rt_thread_startup(tid);
+    }
+    _akrun = (ret == 0);
+
+    return ret;
+}
+
+static void airkiss(int c, char **v)
+{
+    static akcap_t cap = {0};
+    int chn = 0;
+    int op = 0;
+
+    while (c)
+    {
+        switch (c)
+        {
+        case 4:
+            cap.omode = atoi(&(v[3][1]));
+            break;
+        case 3:
+            chn = atoi(v[2]);
+            break;
+        case 2:
+            op = v[1][0];
+            break;
+        }
+
+        c--;
+    }
+
+    if (op == 0)
+    {
+        airkiss_demo_start();
+    }
+    else if (op == 'c')
+    {
+        if (chn == 0)
+        {
+            cap.schn = 1;
+            cap.echn = 12;
+        }
+        else
+        {
+            cap.schn = chn;
+            cap.echn = chn;
+        }
+        cap.isrun = 1;
+        airkiss_cap_start(&cap);
+    }
+}
+MSH_CMD_EXPORT(airkiss, start airkiss);
+#endif