【程序】Marvell 88W8686 WiFi模块(WM-G-MR-09)创建或连接热点,并使用lwip2.0.3建立http服务器(20180129版)

该程序是旧版本!最新版本为20180706版:

https://blog.csdn.net/ZLK1214/article/details/80941657

本程序所用的单片机型号为:STM32F103RE

PB12端口为外接的WiFi模块电源开关,当PB12输出低电平时接通电源。WiFi模块的电源引脚VCC不可直接连接到电源上,必须要串联一组PNP三极管(或场效应管),并把基极接到PB12端口上,基极要接限流电阻。

注意:WM-G-MR-09模块的芯片组(Chip Set)就是Marvell 88W8686。

Keil5工程下载地址:https://pan.baidu.com/s/1miZh6ha
代码说明:http://blog.csdn.net/ZLK1214/article/details/79278871


【勘误】

2018年2月3日:while (WiFi_GetPacketLength() == 0xfedc);中的==应该改为!=

                       WiFi_GetPacketLength函数应该更名为WiFi_GetDataLength函数,否则可能会引起歧义。该函数的作用是获取Wi-Fi模块准备发给主机的数据(uploading data)的大小,而非以太网数据帧(packet)的大小

2018年2月6日:if (((WiFi_CommandHeader *)wifi_rx)->seq_num == rx_cmd->seq_num)
                       应该改为 if (rx_cmd->seq_num == tx_cmd->seq_num)

2018年2月25日:WiFi_Wait函数中以下两句话必须去掉

if ((WiFi_LowLevel_ReadReg(1, WIFI_INTSTATUS) & status) == 0) // 没有其他中断标志位时
  WiFi_LowLevel_ClearSDIOIT(); // 清除单片机上的中断标志位 (在此处清除可避免下面的操作错过新的中断)

因为读INTSTATUS寄存器会自动撤销SDIO_D1上的中断信号,并且只有在DPSM处于Idle状态下(DTEN=0,或“DTEN=1且数据已传输完毕”)SDIO_D1引脚(PC9)上有下降沿产生时,SDIO_STA_SDIOIT位才会置位。如果清除SDIOIT前刚好有其他中断到来,SDIOIT就永远也不会再次置位,SDIO_D1保持低电平,使程序卡死

2018年2月27日:STM32的SDIO中断检测功能只能检测DPSM处于Idle状态时SDIO_D1引脚上的下降沿信号。使用CMD53命令接收数据时,必须先发送CMD53命令,在命令的发送过程中就将DTEN置1,让DPSM准备好接收数据。SDIO设备会在发送CMD53命令回应的同时开始发送数据,因此程序不能等到CMDREND位置位(已收到命令回应)后才打开DPSM,否则会因数据接收不完整导致CRC校验错误。然而,SDIO标准规定,CMD53命令的停止位发送结束时SDIO_D1的中断复用周期才结束。只要将DTEN置1,DPSM就会离开Idle状态进入Wait_R状态,停止检测中断信号。如果SDIO_D1引脚上的下降沿信号出现在CMD53命令开始发送到发送完毕(接收回应前)期间,那么STM32将无法检测到这个信号,并把这个信号错误地识别为STBITERR错误。因此,程序不能完全依靠SDIO_STA_SDIOIT标志位判断是否有新中断产生,必须在WiFi_CheckTimeout函数中定期调用WiFi_Input函数检查WIFI_INTSTATUS寄存器的状态。

2018年3月6日:EAPOLKeyFrame结构中,version和descriptor_type的取值与WPA的版本号没有关系(请参阅IEEE Std 802.1X-2010文档的11.3 Common EAPOL PDU structure和11.9 EAPOL-Key两节的描述)

2018年3月8日:delay函数中while (sys_now() <= nms)应该改为while (sys_now() <= newtime)

                              EAPOL认证中发出的MSG2应该添加RSN IE信息作为Key Data,如果没有RSN IE信息则添加WPA IE信息

                              EAPOL认证中处理MSG3时,如果提取GTK失败,程序不应该break退出,而是应该调用WiFi_SetKeyMaterial函数把PTK发给固件,然后给路由器发送MSG4回应

                              WiFi_ExtractGTK函数中应该根据keydata_len == key_len条件判断解密后的Key Data是否就是GTK

2018年3月10日:有时候连接热点时CMD_802_11_ASSOCIATE和CMD_802_11_SCAN命令无论怎么重发都无法收到回应,原因是WiFi.h中命令回应的默认超时时间太短,应该改成更大的数值。WIFI_DEFAULT_TIMEOUT_CMDRESP应改为1000


Wi-Fi模块电源引脚的连接方法:



程序支持连接无密码的热点以及WEP、WPA-PSK和WPA2-PSK认证类型的热点,加密方式支持TKIP和AES。
支持创建无密码或是带有WEP密码的ADHOC热点,ADHOC模式下不支持WPA和WPA2!

注意:虽然SDIO标准规定可以总线上可以接多张SD卡,但STM32单片机的SDIO接口只支持接一张卡,STM32F103芯片手册Datasheet(不是参考手册)中有声明:

The current version supports only one SD/SDIO/MMC4.2 card at any one time and a stack of MMC4.1 or previous.
如果想要同时使用WiFi模块和SD内存卡,建议SD内存卡采用SPI总线通信。

【程序运行截图】

连上路由器后DHCP分配得到IP地址:(花的时间有时候长,有时候短,这个问题亟待解决。。。)

下面是把WiFi模块固件写入单片机芯片Flash固定区域的程序(用于减少调试主程序时下载程序的时间)的运行结果:

电脑上ping IP地址和计算机名:

通过计算机名在电脑上访问开发板上的HTTP服务器(lwip自带的httpd):

【程序运行结果】

STM32F103RE SDIO 88W8686
Timeout! Resend CMD5!
Timeout! Resend CMD5!
Timeout! Resend CMD5!
Timeout! Resend CMD5!
RESPCMD63, RESP1_90ff8000
RESPCMD63, RESP1_90300000
Number of I/O Functions: 1
Memory Present: 0
Relative Card Address: 0x0001
Card selected! RESP1_00001e00
SDIO Clock: 24MHz
Product Information: Marvell 802.11 SDIO ID: 0B
Manufacturer Code: 0x02df
Manufacturer Information: 0x9103
Card Function Code: 0x0c
System Initialization Bit Mask: 0x00
Maximum Block Size: 256
Maximum Transfer Rate Code: 0x32
Firmware is successfully downloaded!
WiFi Command 0x004d Timeout! Resend...
MAC Addr: 00:1A:6B:A4:AA:B4
SSID 'CMCC-EDU', MAC C6:14:4B:57:DA:5D, RSSI 74, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC C6:14:4B:57:DA:5E, RSSI 74, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 66:14:4B:62:E8:87, RSSI 69, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 66:14:4B:62:E8:88, RSSI 69, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC B6:14:4B:62:BC:8C, RSSI 65, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC B6:14:4B:62:BC:8D, RSSI 65, Channel 1
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID '??????', MAC C4:36:55:32:AA:21, RSSI 62, Channel 1
  Capability: 0x0431 (Security: WPA2, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 66:14:4B:57:FE:57, RSSI 66, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 66:14:4B:57:FE:58, RSSI 66, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 96:14:4B:66:07:5A, RSSI 59, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 96:14:4B:66:07:5B, RSSI 58, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 06:14:4B:62:BC:A1, RSSI 82, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 06:14:4B:62:BC:A2, RSSI 82, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC E6:14:4B:58:06:D0, RSSI 74, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 16:14:4B:65:FA:B2, RSSI 79, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC E6:14:4B:58:06:DF, RSSI 78, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 16:14:4B:65:FA:B3, RSSI 78, Channel 6
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 56:14:4B:62:E9:E6, RSSI 69, Channel 11
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'suf_yuhao', MAC E4:F3:F5:21:55:DC, RSSI 65, Channel 11
  Capability: 0x0431 (Security: WPA2, Mode: Infrastructure)
SSID 'Oct1158-2', MAC FC:D7:33:FE:D6:02, RSSI 38, Channel 11
  Capability: 0x0431 (Security: WPA2, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 56:14:4B:62:E9:E7, RSSI 70, Channel 11
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-EDU', MAC 46:14:4B:58:07:15, RSSI 75, Channel 11
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
SSID 'CMCC-Young', MAC 46:14:4B:58:07:16, RSSI 76, Channel 11
  Capability: 0x0621 (Security: Unsecured, Mode: Infrastructure)
Scan finished!
Waiting for authentication!
Message 1 received!
Message 2 sent!
Message 3 received!
PTK & GTK set!
Message 4 sent!
Authenticated!
[Send] len=350
[Recv] len=42
[Recv] len=590
[Send] len=350
[Recv] len=590
[Send] len=42
[Recv] len=42
[Recv] len=42
[Recv] len=42
[Recv] len=342
[Recv] len=42
[Recv] len=42
[Send] len=42
[Send] len=42
[Send] len=42
DHCP supplied address at 50.81s!
IP address: 192.168.1.3
Subnet mask: 255.255.255.0
Default gateway: 192.168.1.1
DNS Server: 183.221.253.100
[Send] len=42
-- WiFi Packet Timeout! Resend...
Not in cache! err=-5
[Recv] len=42
[Send] len=76
[Recv] len=92
DNS Found IP: 106.186.126.193
Connecting to 106.186.126.193...
[Send] len=58
[Recv] len=58
Connected! err=0
Connection is successfully closed!
[Send] len=54
[Recv] len=54
[Send] len=54
[Recv] len=74
[Send] len=58
[Recv] len=54
[Recv] len=340
[Send] len=590
[Send] len=590
[Recv] len=54
[Send] len=590
[Recv] len=54
[Send] len=304
[Recv] len=54
[Recv] len=54
[Recv] len=54
[Send] len=54
[Recv] len=342
[Recv] len=42
[Recv] len=42
[Recv] len=92
[Send] len=42
[Recv] len=42
[Send] len=104
[Recv] len=42
[Send] len=42
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=74
[Send] len=74
[Recv] len=42
[Send] len=42
[Recv] len=253
[Recv] len=92
[Recv] len=92
[Recv] len=92
[Recv] len=66
[Send] len=58
[Recv] len=54
[Recv] len=374
[Send] len=590
[Send] len=590
[Recv] len=54
[Send] len=590
[Send] len=304
[Recv] len=54
[Recv] len=54
[Send] len=54
[Recv] len=66
[Send] len=58
[Recv] len=54
[Recv] len=326
[Send] len=590
[Send] len=349
[Recv] len=54
[Recv] len=54
[Send] len=54
[Recv] len=66
[Send] len=58
[Recv] len=54
[Recv] len=355
[Send] len=590
[Send] len=202
[Recv] len=54
[Recv] len=54
[Send] len=54
[Recv] len=342
[Recv] len=66
[Send] len=58
[Recv] len=54
[Recv] len=300
[Send] len=590
[Send] len=590
[Recv] len=54
[Send] len=590
[Recv] len=54
[Send] len=304
[Recv] len=54
[Recv] len=66
[Send] len=58
[Recv] len=54
[Recv] len=54
[Recv] len=355
[Send] len=590
[Send] len=349
[Recv] len=54
[Recv] len=54
[Recv] len=54
[Send] len=54
[Recv] len=54
[Send] len=54
Connection closed!

【程序的主要代码】

WiFi_LowLevel.c:

#include <stdio.h>
#include <stm32f10x.h>
#include <string.h>
#include "common.h"
#include "WiFi.h"

#define SDIO_SUCCEEDED() ((SDIO->STA & ~SDIO_STA_SDIOIT) == 0)

#define CMD52_WRITE _BV(31)
#define CMD52_READAFTERWRITE _BV(27)
#define CMD53_WRITE _BV(31)
#define CMD53_BLOCKMODE _BV(27)
#define CMD53_INCREMENTING _BV(26)

static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize);
static void WiFi_LowLevel_GPIOInit(void);
static void WiFi_LowLevel_SDIOInit(void);
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags);
static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags);
static void WiFi_LowLevel_SetSDIOBlockSize(uint32_t size);
#ifdef WIFI_FIRMWAREAREA_ADDR
static uint8_t WiFi_LowLevel_VerifyFirmware(void);
#endif

static uint16_t sdio_block_size[2]; // 各功能区的块大小, 保存在此变量中避免每次都去发送CMD52命令读SDIO寄存器
static uint16_t sdio_func_num;
static uint16_t sdio_rca; // 虽然SDIO标准规定SDIO接口上可以接多张SD卡, 但是STM32的SDIO接口只能接一张卡 (芯片手册上有说明)

void WiFi_LowLevel_ClearSDIOIT(void)
{
  SDIO->ICR = SDIO_ICR_SDIOITC;
}

static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize)
{
  uint16_t block_num = 0;
#ifndef WIFI_HIGHSPEED
  if (*psize > 512 || *psize % sdio_block_size[func] == 0) // 若size为数据块大小的整数倍, 则采用数据块方式发送
  {
#endif
    // 块传输模式 (DTMODE=0)
    WiFi_LowLevel_SetSDIOBlockSize(sdio_block_size[func]);
    
    block_num = *psize / sdio_block_size[func];
    if (*psize % sdio_block_size[func] != 0)
      block_num++; // 大于512字节时不能采用字节流方式发送(如712字节), 仍采用数据块方式
    *psize = block_num * sdio_block_size[func]; // 块数*块大小
#ifndef WIFI_HIGHSPEED
  }
  else
  {
    // 多字节传输模式 (DTMODE=1, SDIO的频率低于16MHz时才支持)
    *psize = (*psize + 3) & ~3; // WiFi模块要求写入的字节数必须为4的整数倍
  }
#endif
  return block_num;
}

uint8_t WiFi_LowLevel_GetFunctionNum(void)
{
  return sdio_func_num;
}

 /* 初始化WiFi模块有关的所有GPIO引脚 */
static void WiFi_LowLevel_GPIOInit(void)
{
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;
  
  // WiFi模块的电源引脚是通过场效应管(相当于PNP三极管)接到VCC上的
  // 基极接的是单片机的PB12, 发射极接的是电源VCC, 集电极接的是WiFi模块的VCC
  // 单片机复位时PB12输出高阻态, 三极管不导通, WiFi模块不通电
  // 现将PB12设为输出低电平, 三极管导通, WiFi模块上电 (这起到了复位的效果)
  GPIOB->CRH = (GPIOB->CRH & 0xfff0ffff) | 0x30000; // PB12设为推挽输出(3), 并立即输出默认的低电平
  
  // SDIO相关引脚
  GPIOC->CRH = (GPIOC->CRH & 0xfff00000) | 0xbbbbb; // PC8~11: SDIO_D0~3, PC12: SDIO_CK, 设为复用推挽输出(b)
  GPIOD->CRL = (GPIOD->CRL & 0xfffff0ff) | 0xb00; // PD2: SDIO_CMD, 设为复用推挽输出
}

void WiFi_LowLevel_Init(void)
{
  // 在此处打开WiFi模块所需要的除GPIO和SDIO外所有其他外设的时钟
  RCC->AHBENR |= RCC_AHBENR_CRCEN;
  
#ifdef WIFI_FIRMWAREAREA_ADDR
  if (!WiFi_LowLevel_VerifyFirmware())
  {
    printf("Error: The firmware stored in flash memory is corrupted!\n");
    while (1);
  }
#endif
  
  WiFi_LowLevel_GPIOInit();
  WiFi_LowLevel_SDIOInit();
}

uint8_t WiFi_LowLevel_ReadData(uint8_t func, uint32_t addr, void *data, uint32_t size, uint32_t bufsize)
{
  uint16_t block_num; // 数据块个数
#ifdef WIFI_USEDMA
  uint32_t temp; // 丢弃数据用的变量
#else
  uint32_t *p = data;
#endif
  
  block_num = WiFi_LowLevel_GetBlockNum(func, &size);
  if (bufsize > 0 && bufsize < size)
  {
    printf("WiFi_LowLevel_ReadData: a buffer of at least %d bytes is required! bufsize=%d\n", size, bufsize);
    return 0;
  }
  
#ifdef WIFI_USEDMA
  DMA2_Channel4->CPAR = (uint32_t)&SDIO->FIFO;
  DMA2_Channel4->CNDTR = size / 4;
  DMA2_Channel4->CCR = DMA_CCR4_PL | DMA_CCR4_MSIZE_1 | DMA_CCR4_PSIZE_1;
  if (bufsize > 0)
  {
    DMA2_Channel4->CMAR = (uint32_t)data;
    DMA2_Channel4->CCR |= DMA_CCR4_MINC;
  }
  else
    DMA2_Channel4->CMAR = (uint32_t)&temp; // 数据丢弃模式
  DMA2_Channel4->CCR |= DMA_CCR4_EN;
#endif
  
  if (block_num)
  {
    SDIO->DCTRL &= ~SDIO_DCTRL_DTMODE;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, CMD53_BLOCKMODE);
  }
  else
  {
    SDIO->DCTRL |= SDIO_DCTRL_DTMODE;
    WiFi_LowLevel_SendCMD53(func, addr, size, 0);
  }
  
  SDIO->DLEN = size;
  SDIO->DCTRL |= SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
  
#ifdef WIFI_USEDMA
  while ((DMA2->ISR & DMA_ISR_TCIF4) == 0);
  DMA2->IFCR = DMA_IFCR_CTCIF4;
  DMA2_Channel4->CCR &= ~DMA_CCR4_EN;
#else
  while (size)
  {
    // 如果有数据到来就读取数据
    if (SDIO->STA & SDIO_STA_RXDAVL)
    {
      size -= 4;
      if (bufsize > 0)
        *p++ = SDIO->FIFO;
      else
        SDIO->FIFO; // 读寄存器, 但不保存数据
    }
  }
#endif
  
  while (SDIO->STA & (SDIO_STA_CMDACT | SDIO_STA_RXACT)); // 等待接收完毕
  SDIO->DCTRL &= ~SDIO_DCTRL_DTEN;
  
  SDIO->ICR = SDIO_STA_DATAEND | SDIO_ICR_CMDRENDC;
  if (block_num)
    SDIO->ICR = SDIO_ICR_DBCKENDC;
  
  SDIO->STA; // 读状态寄存器后标志位才能真正清除
  return SDIO_SUCCEEDED();
}

/* 读SDIO寄存器 */
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr)
{
  WiFi_LowLevel_SendCMD52(func, addr, NULL, NULL);
  while (SDIO->STA & SDIO_STA_CMDACT);
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    return SDIO->RESP1 & 0xff;
  }
  else
  {
    printf("WiFi_LowLevel_ReadReg failed!\n");
    return 0;
  }
}

/* 初始化SDIO外设并完成WiFi模块的枚举 */
// SDIO Simplified Specification Version 3.00: 3. SDIO Card Initialization
static void WiFi_LowLevel_SDIOInit(void)
{
  // SDIO外设拥有两个时钟: SDIOCLK=HCLK=72MHz(分频后用于产生SDIO_CK=PC12引脚时钟), AHB bus clock=HCLK/2=36MHz
  RCC->AHBENR |= RCC_AHBENR_SDIOEN;
#ifdef WIFI_USEDMA
  RCC->AHBENR |= RCC_AHBENR_DMA2EN;
#endif
  
  SDIO->POWER = SDIO_POWER_PWRCTRL; // 打开SDIO外设
  SDIO->CLKCR = SDIO_CLKCR_CLKEN | 178; // 初始化时最高允许的频率: 72MHz/(178+2)=400kHz
  SDIO->DCTRL = SDIO_DCTRL_SDIOEN; // 设为SDIO模式
#ifdef WIFI_USEDMA
  SDIO->DCTRL |= SDIO_DCTRL_DMAEN; // 必须在DPSM禁用的时候打开DMA请求
#endif
  
  // 不需要发送CMD0, 因为SD I/O card的初始化命令是CMD52
  // An I/O only card or the I/O portion of a combo card is NOT reset by CMD0. (See 4.4 Reset for SDIO)
  delay(10); // 延时可防止CMD5重发
  
  /* 发送CMD5: IO_SEND_OP_COND */
  SDIO->ARG = 0;
  SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 5; // 接收短回应
  while (SDIO->STA & SDIO_STA_CMDACT); // 等待命令发送完毕
  while (SDIO->STA & SDIO_STA_CTIMEOUT) // CMD5超时处理
  {
    SDIO->ICR = SDIO_ICR_CTIMEOUTC; // 清除标志
    printf("Timeout! Resend CMD5!\n");
    delay(5);
    SDIO->CMD = SDIO->CMD; // 重发
    while (SDIO->STA & SDIO_STA_CMDACT);
  }
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    printf("RESPCMD%d, RESP1_%08x\n", SDIO->RESPCMD, SDIO->RESP1);
  }
  
  /* 设置参数VDD Voltage Window: 3.2~3.4V, 并再次发送CMD5 */
  SDIO->ARG = 0x300000;
  SDIO->CMD = SDIO->CMD;
  while (SDIO->STA & SDIO_STA_CMDACT);
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    printf("RESPCMD%d, RESP1_%08x\n", SDIO->RESPCMD, SDIO->RESP1);
    if (SDIO->RESP1 & _BV(31))
    {
      // Card is ready to operate after initialization
      sdio_func_num = (SDIO->RESP1 >> 28) & 7;
      printf("Number of I/O Functions: %d\n", sdio_func_num);
      printf("Memory Present: %d\n", (SDIO->RESP1 & _BV(27)) != 0);
    }
  }
  
  /* 获取WiFi模块地址 (CMD3: SEND_RELATIVE_ADDR, Ask the card to publish a new relative address (RCA)) */
  SDIO->ARG = 0;
  SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 3;
  while (SDIO->STA & SDIO_STA_CMDACT);
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    sdio_rca = SDIO->RESP1 >> 16;
    printf("Relative Card Address: 0x%04x\n", sdio_rca);
  }
  
  /* 选中WiFi模块 (CMD7: SELECT/DESELECT_CARD) */
  SDIO->ARG = sdio_rca << 16;
  SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 7;
  while (SDIO->STA & SDIO_STA_CMDACT);
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    printf("Card selected! RESP1_%08x\n", SDIO->RESP1);
  }
  
  /* 提高时钟频率, 超时时间为0.1s */
#ifdef WIFI_HIGHSPEED
  SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 1; // 72MHz/(1+2)=24MHz
  SDIO->DTIMER = 2400000;
  printf("SDIO Clock: 24MHz\n");
#else
  SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 70; // 72MHz/(70+2)=1MHz
  SDIO->DTIMER = 100000;
  printf("SDIO Clock: 1MHz\n");
#endif
  
  // SDIO外设的总线宽度设为4位
  SDIO->CLKCR |= SDIO_CLKCR_WIDBUS_0;
  WiFi_LowLevel_WriteReg(0, SDIO_CCCR_BUSIFCTRL, WiFi_LowLevel_ReadReg(0, SDIO_CCCR_BUSIFCTRL) | SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit);
}

static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags)
{
  SDIO->ARG = (func << 28) | (addr << 9) | data | flags;
  SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 52;
}

static void WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags)
{
  // 当count=512时, 和0x1ff相与后为0, 符合要求
  SDIO->ARG = (func << 28) | (addr << 9) | (count & 0x1ff) | flags;
  SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 53;
}

/* 设置WiFi模块功能区的数据块大小 */
void WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size)
{
  sdio_block_size[func] = size;
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x10, size & 0xff);
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x11, size >> 8);
}

/* 设置SDIO外设的数据块大小 */
static void WiFi_LowLevel_SetSDIOBlockSize(uint32_t size)
{
  uint8_t i;
  for (i = 0; (size & 1) == 0; i++)
    size /= 2;
  SDIO->DCTRL = (SDIO->DCTRL & ~SDIO_DCTRL_DBLOCKSIZE) | (i << 4);
}

/* 检查Flash中保存的固件内容是否完整 */
#ifdef WIFI_FIRMWAREAREA_ADDR
static uint8_t WiFi_LowLevel_VerifyFirmware(void)
{
  const uint8_t *data;
  uint32_t len;
  
  CRC->CR = CRC_CR_RESET;
  data = (const uint8_t *)WIFI_FIRMWAREAREA_ADDR;
  len = (WIFI_HELPER_SIZE + WIFI_FIRMWARE_SIZE) / 4 + 3; // 固件区(包括CRC)总大小的1/4
  while (len--)
  {
    CRC->DR = *(uint32_t *)data;
    data += 4;
  }
  return CRC->DR == 0;
}
#endif

/* 发送数据, 自动判断采用哪种传输模式 */
// count为要发送的字节数或块数, bufsize为data缓冲区的大小
uint8_t WiFi_LowLevel_WriteData(uint8_t func, uint32_t addr, const void *data, uint32_t size, uint32_t bufsize)
{
  uint16_t block_num; // 数据块个数
#ifndef WIFI_USEDMA
  const uint32_t *p = data;
#endif

  block_num = WiFi_LowLevel_GetBlockNum(func, &size);
  if (bufsize < size)
    printf("WiFi_LowLevel_WriteData: a buffer of at least %d bytes is required! bufsize=%d\n", size, bufsize); // 只读缓冲区越界不会影响数据传输, 所以这只是一个警告
  
  if (block_num)
  {
    SDIO->DCTRL &= ~SDIO_DCTRL_DTMODE;
    WiFi_LowLevel_SendCMD53(func, addr, block_num, CMD53_WRITE | CMD53_BLOCKMODE);
  }
  else
  {
    SDIO->DCTRL |= SDIO_DCTRL_DTMODE;
    WiFi_LowLevel_SendCMD53(func, addr, size, CMD53_WRITE);
  }
  while (SDIO->STA & SDIO_STA_CMDACT); // 必须要等到CMD53收到回应后才能开始发送数据
  if ((SDIO->STA & SDIO_STA_CMDREND) == 0)
  {
    printf("WiFi_LowLevel_WriteData: CMD53 no response!\n");
    return 0;
  }
  SDIO->ICR = SDIO_ICR_CMDRENDC;
  
  // 开始发送数据
#ifdef WIFI_USEDMA
  DMA2_Channel4->CMAR = (uint32_t)data;
  DMA2_Channel4->CPAR = (uint32_t)&SDIO->FIFO;
  DMA2_Channel4->CNDTR = size / 4;
  DMA2_Channel4->CCR = DMA_CCR4_PL | DMA_CCR4_MSIZE_1 | DMA_CCR4_PSIZE_1 | DMA_CCR4_MINC | DMA_CCR4_DIR | DMA_CCR4_EN;
#endif
  
  SDIO->DLEN = size;
  SDIO->DCTRL = (SDIO->DCTRL & ~SDIO_DCTRL_DTDIR) | SDIO_DCTRL_DTEN;
  
#ifdef WIFI_USEDMA
  while ((DMA2->ISR & DMA_ISR_TCIF4) == 0);
  DMA2->IFCR = DMA_IFCR_CTCIF4;
  DMA2_Channel4->CCR &= ~DMA_CCR4_EN;
#else
  while (size)
  {
    size -= 4;
    SDIO->FIFO = *p++; // 向FIFO送入4字节数据
    while (SDIO->STA & SDIO_STA_TXFIFOF); // 如果FIFO已满则等待
  }
#endif
  
  while (SDIO->STA & SDIO_STA_TXACT); // 等待发送完毕
  SDIO->DCTRL &= ~SDIO_DCTRL_DTEN; // 数据传输完毕后DTEN应及时清零, 防止后续对DCTRL寄存器操作后误启动数据传输导致超时或CRC校验错误
  
  // 清除相关标志位
  SDIO->ICR = SDIO_ICR_DATAENDC;
  if (block_num)
    SDIO->ICR = SDIO_ICR_DBCKENDC;
  
  SDIO->STA; // 读状态寄存器后标志位才能真正清除
  return SDIO_SUCCEEDED();
}

/* 写寄存器, 返回写入后寄存器的实际内容 */
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value)
{
  WiFi_LowLevel_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE);
  while (SDIO->STA & SDIO_STA_CMDACT);
  if (SDIO->STA & SDIO_STA_CMDREND)
  {
    SDIO->ICR = SDIO_ICR_CMDRENDC;
    return SDIO->RESP1 & 0xff;
  }
  else
  {
    printf("WiFi_LowLevel_WriteReg failed!\n");
    return 0;
  }
}

WiFi.c:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "WPA.h"
#include "WiFi.h"

static void WiFi_Associate_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_AssociateEx_Callback(void *arg, void *data, WiFi_Status status);
static uint8_t WiFi_CheckCommandBusy(WiFi_Callback callback, void *arg);
static uint8_t WiFi_CheckMIC(WiFi_EAPOLKeyFrame *packet, uint16_t len);
static uint8_t WiFi_CheckTxBufferRetry(WiFi_TxBuffer *tbuf, void *data);
static void WiFi_DownloadFirmware(void);
static void WiFi_EAPOLProcess(WiFi_DataRx *data);
static void WiFi_EAPOLProcess_Callback(void *arg, void *data, WiFi_Status status);
static uint8_t WiFi_ExtractGTK(const WiFi_EAPOLKeyFrame *packet_rx);
static void WiFi_GetMACAddress_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_JoinADHOC_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_JoinADHOCEx_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_TxBufferComplete(WiFi_TxBuffer *tbuf, void *data, WiFi_Status status);
static void WiFi_Scan_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_ScanSSID_Callback(void *arg, void *data, WiFi_Status status);
static void WiFi_SendEAPOLResponse(const WiFi_EAPOLKeyFrame *packet_rx, uint16_t key_info, WiFi_Callback callback, void *arg);
static void WiFi_SetKeyMaterial(WiFi_KeyType key_type, uint8_t key_num, WiFi_Callback callback, void *arg);
static void WiFi_StartADHOCEx_Callback(void *data, void *arg, WiFi_Status status);

static uint8_t wifi_psk[32]; // preshared-key
static uint8_t wifi_rx[2048]; // 帧接收缓冲区
static uint8_t wifi_snonce[32];
static uint32_t wifi_port;
static WiFi_GTK wifi_gtk;
static WiFi_PTK wifi_ptk;
static WiFi_TxBuffer wifi_tx_command = {0}; // 命令帧发送缓冲区
static WiFi_TxBuffer wifi_tx_packet = {0}; // 数据帧发送缓冲区

/* 关联一个热点 */
// 参数mac_addr用于接收热点的MAC地址, 可以为NULL, 但是不能指向局部变量
void WiFi_Associate(const char *ssid, WiFi_AuthenticationType auth_type, uint8_t *mac_addr, WiFi_Callback callback, void *arg)
{
  void **p;
  WiFi_SSIDInfo *info;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  p = malloc(4 * sizeof(void *) + sizeof(WiFi_SSIDInfo)); // 最后一个成员不是指针, 而是实际数据
  if (p == NULL)
  {
    printf("WiFi_Associate: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = (void *)auth_type;
  p[3] = mac_addr;
  info = (WiFi_SSIDInfo *)(p + 4);
  
  WiFi_ScanSSID(ssid, info, WiFi_Associate_Callback, p);
}

static void WiFi_Associate_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg; // 之前分配的内存
  void *app_arg = p[0]; // 用户指定的参数
  WiFi_Callback app_callback = (WiFi_Callback)p[1]; // 用户指定的回调函数
  WiFi_AuthenticationType auth_type = (WiFi_AuthenticationType)(uint32_t)p[2];
  uint8_t *mac_addr = (uint8_t *)p[3];
  WiFi_SSIDInfo *info = (WiFi_SSIDInfo *)(p + 4); // SSID信息
  
  uint16_t cmd_size;
  WiFi_CmdRequest_Associate *cmd;
  WiFi_CmdResponse_Associate *resp;
  WiFi_SecurityType security;
  MrvlIETypes_PhyParamDSSet_t *ds;
  MrvlIETypes_CfParamSet_t *cf;
  MrvlIETypes_AuthType_t *auth;
  MrvlIETypes_VendorParamSet_t *vendor;
  MrvlIETypes_RsnParamSet_t *rsn;
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_Associate error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  security = WiFi_GetSecurityType(info);
  switch (WiFi_GetCommandCode(data))
  {
    case CMD_802_11_SCAN:
      // WiFi_ScanSSID命令执行完毕
      cmd = (WiFi_CmdRequest_Associate *)wifi_tx_command.buffer;
      memcpy(cmd->peer_sta_addr, info->mac_addr, sizeof(info->mac_addr));
      cmd->cap_info = info->cap_info;
      cmd->listen_interval = 10;
      cmd->bcn_period = info->bcn_period;
      cmd->dtim_period = 1;
      memcpy(cmd + 1, &info->ssid, TLV_STRUCTLEN(info->ssid));
      
      ds = (MrvlIETypes_PhyParamDSSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info->ssid));
      ds->header.type = WIFI_MRVLIETYPES_DSPARAMSET;
      ds->header.length = 1;
      ds->channel = info->channel;
      
      cf = (MrvlIETypes_CfParamSet_t *)(ds + 1);
      memset(cf, 0, sizeof(MrvlIETypes_CfParamSet_t));
      cf->header.type = WIFI_MRVLIETYPES_CFPARAMSET;
      cf->header.length = TLV_PAYLOADLEN(*cf);
      
      memcpy(cf + 1, &info->rates, TLV_STRUCTLEN(info->rates));
      auth = (MrvlIETypes_AuthType_t *)((uint8_t *)(cf + 1) + TLV_STRUCTLEN(info->rates));
      auth->header.type = WIFI_MRVLIETYPES_AUTHTYPE;
      auth->header.length = TLV_PAYLOADLEN(*auth);
      auth->auth_type = auth_type;
      
      cmd_size = (uint8_t *)(auth + 1) - wifi_tx_command.buffer;
      if (security == WIFI_SECURITYTYPE_WPA)
      {
        // WPA网络必须在命令中加入Vendor参数才能成功连接
        vendor = (MrvlIETypes_VendorParamSet_t *)(auth + 1);
        memcpy(vendor, &info->wpa, TLV_STRUCTLEN(info->wpa));
        cmd_size += TLV_STRUCTLEN(info->wpa);
      }
      else if (security == WIFI_SECURITYTYPE_WPA2)
      {
        // WPA2网络必须在命令中加入RSN参数才能成功连接
        rsn = (MrvlIETypes_RsnParamSet_t *)(auth + 1);
        memcpy(rsn, &info->rsn, TLV_STRUCTLEN(info->rsn));
        cmd_size += TLV_STRUCTLEN(info->rsn);
      }
      
      WiFi_SendCommand(CMD_802_11_ASSOCIATE, wifi_tx_command.buffer, cmd_size, WiFi_Associate_Callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
      // 保留arg内存, 等关联成功后再释放
      break;
    case CMD_802_11_ASSOCIATE:
      // 关联命令执行完毕并收到了回应
      // 现在需要检查是否关联成功
      if (mac_addr != NULL)
        memcpy(mac_addr, info->mac_addr, 6);
      free(arg); // arg释放掉之后, info指针也不再可用
      resp = (WiFi_CmdResponse_Associate *)data;
      //printf("capability=0x%04x, status_code=0x%04x, aid=0x%04x\n", resp->capability, resp->status_code, resp->association_id);
      if (app_callback)
      {
        if (resp->association_id == 0xffff)
          app_callback(app_arg, data, WIFI_STATUS_FAIL); // 关联失败 (在回调函数的data中检查resp->capability和resp->status_code的值可获得详细原因)
        else if (security == WIFI_SECURITYTYPE_WPA || security == WIFI_SECURITYTYPE_WPA2)
          app_callback(app_arg, data, WIFI_STATUS_INPROGRESS); // 等待认证
        else
          app_callback(app_arg, data, WIFI_STATUS_OK); // 关联成功
      }
      break;
  }
}

/* 关联一个热点并输入密码 */
// 连接WPA型的热点时, security成员直接赋值WIFI_SECURITYTYPE_WPA即可, 不需要明确指出WPA版本号
void WiFi_AssociateEx(const WiFi_Connection *conn, WiFi_AuthenticationType auth_type, int32_t max_retry, WiFi_Callback callback, void *arg)
{
  uint16_t ssid_len;
  void **p;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  ssid_len = strlen(conn->ssid);
  p = malloc(5 * sizeof(void *) + ssid_len + 1);
  if (p == NULL)
  {
    printf("WiFi_AssociateEx: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = (void *)auth_type;
  p[3] = (void *)max_retry; // 最大尝试重新连接的次数, -1表示无限次数, 0表示不重试
  p[4] = conn->mac_addr;
  memcpy(p + 5, conn->ssid, ssid_len + 1);
  
  if (conn->security == WIFI_SECURITYTYPE_WEP)
    WiFi_SetWEP(WIFI_ACT_ADD, conn->password, WiFi_AssociateEx_Callback, p);
  else
  {
    WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_AssociateEx_Callback, p);
    if (conn->security == WIFI_SECURITYTYPE_WPA || conn->security == WIFI_SECURITYTYPE_WPA2)
      WiFi_SetWPA(conn->ssid, conn->password);
  }
}

static void WiFi_AssociateEx_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  WiFi_AuthenticationType auth_type = (WiFi_AuthenticationType)(uint32_t)p[2];
  int32_t *pmax_retry = (int32_t *)(p + 3);
  uint8_t *mac_addr = (uint8_t *)p[4];
  char *ssid = (char *)(p + 5);
  uint16_t cmd_code = WiFi_GetCommandCode(data);
  
  if (cmd_code == CMD_802_11_ASSOCIATE || cmd_code == CMD_802_11_SCAN)
  {
    if (status == WIFI_STATUS_OK || status == WIFI_STATUS_INPROGRESS)
    {
      // 关联成功
      free(arg);
      if (app_callback)
        app_callback(app_arg, data, status);
      return;
    }
    else
    {
      // 关联失败, 重试
      if (*pmax_retry != 0)
      {
        if (*pmax_retry != -1)
          (*pmax_retry)--;
        cmd_code = CMD_MAC_CONTROL;
        status = WIFI_STATUS_OK;
      }
    }
  }
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_AssociateEx error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  switch (cmd_code)
  {
    case CMD_802_11_SET_WEP:
      WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_WEP | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_AssociateEx_Callback, arg);
      break;
    case CMD_MAC_CONTROL:
      WiFi_Associate(ssid, auth_type, mac_addr, WiFi_AssociateEx_Callback, arg);
  }
}

/* 如果之前的命令尚未执行完就请求执行新的命令, 则直接调用回调函数报告错误 */
static uint8_t WiFi_CheckCommandBusy(WiFi_Callback callback, void *arg)
{
  // 发送新命令前必须确保之前的命令已经发送完毕并收到回应
  // See 4.2 Protocol: The command exchange protocol is serialized, the host driver must wait until 
  // it has received a command response for the current command request before it can send the next command request.
  if (WiFi_IsCommandBusy())
  {
    printf("Warning: The previous command is in progress!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_BUSY);
    return 1;
  }
  
  WiFi_WaitForLastTask(); // 等待之前的数据帧收到确认
  return 0;
}

/* 验证EAPOL帧中的MIC值是否正确 */
static uint8_t WiFi_CheckMIC(WiFi_EAPOLKeyFrame *packet, uint16_t len)
{
  uint8_t ret;
  WiFi_KeyType key_type = (WiFi_KeyType)(ntohs(packet->key_information) & 0x07);
  WiFi_MIC mic[2];
  
  memcpy(mic[0].MIC, packet->key_mic, sizeof(packet->key_mic));
  memset(packet->key_mic, 0, sizeof(packet->key_mic));
  if (key_type == WIFI_KEYTYPE_TKIP)
    ret = hmac_md5(wifi_ptk.KCK, sizeof(wifi_ptk.KCK), &packet->version, len - 14, mic[1].MIC);
  else if (key_type == WIFI_KEYTYPE_AES)
    ret = hmac_sha1(wifi_ptk.KCK, sizeof(wifi_ptk.KCK), &packet->version, len - 14, mic[1].MIC);
  else
    return 0;
  
  if (!ret)
  {
    printf("WiFi_CheckMIC: out of memory!\n");
    return 0;
  }
  return memcmp(mic[0].MIC, mic[1].MIC, sizeof(mic[1].MIC)) == 0;
}

/* 数据帧、命令帧发送超时处理 */
void WiFi_CheckTimeout(void)
{
  // 回调函数中的data参数: 失败时为发送失败的数据, 成功时为收到的回应
  // 成功发送数据帧时不会有回应, 此时data为NULL
  WiFi_CommandHeader *cmd = (WiFi_CommandHeader *)wifi_tx_command.buffer;
  
  if (WiFi_CheckTxBufferRetry(&wifi_tx_command, wifi_tx_command.buffer))
  {
    WiFi_SendCommand(0, wifi_tx_command.buffer, 0, wifi_tx_command.callback, wifi_tx_command.arg, wifi_tx_command.timeout, wifi_tx_command.retry - 1);
    printf("WiFi Command 0x%04x Timeout! Resend...\n", cmd->cmd_code);
  }
  if (WiFi_CheckTxBufferRetry(&wifi_tx_packet, wifi_tx_packet.buffer))
  {
    WiFi_SendPacket(NULL, 0, wifi_tx_packet.callback, wifi_tx_packet.arg, wifi_tx_packet.timeout, wifi_tx_packet.retry - 1);
    printf("WiFi Packet Timeout! Resend...\n");
  }
}

/* 检查发送缓冲区是否需要重传 */
// data为报告错误时需要传给回调函数的数据
static uint8_t WiFi_CheckTxBufferRetry(WiFi_TxBuffer *tbuf, void *data)
{
  if (tbuf->busy && sys_now() > tbuf->start_time + tbuf->timeout) // 若超时时间到了
  {
    if (tbuf->retry != 0)
    {
      tbuf->busy = 0;
      return 1;
    }
    else
      WiFi_TxBufferComplete(tbuf, data, WIFI_STATUS_NORESP); // 超过最大重试次数, 向回调函数报告错误
  }
  return 0;
}

/* 与热点断开连接 */
void WiFi_Deauthenticate(const uint8_t mac_addr[6], uint16_t reason, WiFi_Callback callback, void *arg)
{
  WiFi_Cmd_Deauthenticate *cmd = (WiFi_Cmd_Deauthenticate *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  memcpy(cmd->peer_sta_addr, mac_addr, sizeof(cmd->peer_sta_addr));
  cmd->reason_code = reason;
  WiFi_SendCommand(CMD_802_11_DEAUTHENTICATE, wifi_tx_command.buffer, sizeof(WiFi_Cmd_Deauthenticate), callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 固件下载 */
static void WiFi_DownloadFirmware(void)
{
  const uint8_t *data;
  uint16_t curr;
  uint32_t len;
  
  // 下载helper固件
  data = WIFI_HELPER_ADDR;
  len = WIFI_HELPER_SIZE;
  while (len)
  {
    // 每次下载64字节, 其中前4字节为本次下载的数据量
    curr = (len > 60) ? 60 : len;
    memcpy(wifi_tx_packet.buffer, &curr, 4);
    memcpy(wifi_tx_packet.buffer + 4, data, curr);
    
    if (len != WIFI_HELPER_SIZE) // 第一次发送数据前不需要等待
      WiFi_Wait(WIFI_INTSTATUS_DNLD, 0);
    WiFi_LowLevel_WriteData(1, wifi_port, wifi_tx_packet.buffer, 64, sizeof(wifi_tx_packet.buffer));
    len -= curr;
    data += curr;
  }
  memset(wifi_tx_packet.buffer, 0, 4);
  WiFi_LowLevel_WriteData(1, wifi_port, wifi_tx_packet.buffer, 64, sizeof(wifi_tx_packet.buffer)); // 以空数据包结束
  
  // 下载固件
  data = WIFI_FIRMWARE_ADDR;
  len = WIFI_FIRMWARE_SIZE;
  while (len)
  {
    WiFi_Wait(WIFI_INTSTATUS_DNLD, 0);
    while ((curr = WiFi_LowLevel_ReadReg(1, WIFI_SQREADBASEADDR0) | (WiFi_LowLevel_ReadReg(1, WIFI_SQREADBASEADDR1) << 8)) == 0); // 获取本次下载的字节数
    //printf("Required: %d bytes, Remaining: %d bytes\n", curr, len);
    
    if (curr & 1)
    {
      // 若size为奇数(如17), 则说明接收端出现了CRC校验错误, 应重新传送上一次的内容(这部分代码省略)
      printf("Error: an odd size is invalid!\n");
      while (1);
    }
    if (curr > len)
      curr = len;
    
    // 先将Flash中的数据复制到SRAM中, 然后再发送, 避免出现odd size错误甚至Hard Error错误
    memcpy(wifi_tx_packet.buffer, data, curr);
    WiFi_LowLevel_WriteData(1, wifi_port, wifi_tx_packet.buffer, curr, sizeof(wifi_tx_packet.buffer));
    
    len -= curr;
    data += curr;
  }
  
  // 等待Firmware启动
  WiFi_Wait(WIFI_INTSTATUS_DNLD, 0);
  while (WiFi_GetPacketLength() == 0xfedc);
  printf("Firmware is successfully downloaded!\n");
}

/* 丢弃收到但因程序错误一直未处理的数据或命令 */
void WiFi_DiscardData(void)
{
  uint16_t len = WiFi_GetPacketLength();
  WiFi_LowLevel_ReadData(1, wifi_port, NULL, len, 0);
  printf("Discarded %d bytes!\n", len);
}

/* 处理EAPOL认证帧 */
static void WiFi_EAPOLProcess(WiFi_DataRx *data)
{
  uint8_t i, ret;
  uint8_t random_b[10];
  uint16_t key_info;
  uint32_t random_k[16];
  WiFi_EAPOLKeyFrame *packet_rx = (WiFi_EAPOLKeyFrame *)data->payload;
  WiFi_KeyType key_type;
  WiFi_PTKB ptkb;
  
  key_info = ntohs(packet_rx->key_information);
  key_type = (WiFi_KeyType)(key_info & 0x07);
  if (key_type != WIFI_KEYTYPE_TKIP && key_type != WIFI_KEYTYPE_AES)
  {
    printf("Unsupported key descriptor version: %d\n", key_type);
    return;
  }
  
  switch (key_info & 0x23c8) // 移除与EAPOL-Key packet_rx notation前六个参数无关的位
  {
    case 0x88:
      /* 4-way handshake Message 1: EAPOL-Key(0,0,1,0,P,0,...), P=1 */
      printf("Message 1 received!\n");
    
      /* 生成SNonce */
      // PRF-256(Random number, "Init Counter", Local MAC Address || Time)
      srand(sys_now());
      for (i = 0; i < sizeof(random_k) / sizeof(uint32_t); i++)
        random_k[i] = rand();
      memcpy(random_b, packet_rx->dest, sizeof(packet_rx->dest));
      *(uint32_t *)(random_b + sizeof(packet_rx->dest)) = sys_now();
      ret = PRF(random_k, sizeof(random_k), "Init Counter", random_b, sizeof(random_b), wifi_snonce, sizeof(wifi_snonce)); // PRF-256
      if (!ret)
      {
        printf("PRF: out of memory!\n"); // 遇到内存不足的情况, 只需要把STM32启动文件(.s)中的堆空间大小Heap_Size调大就能解决问题
        break;
      }
      //dump_data(snonce, sizeof(snonce));
      
      /* 生成PTK */
      // 较小的MAC地址在前, 较大的在后
      if (memcmp(packet_rx->dest, packet_rx->src, sizeof(packet_rx->src)) < 0)
        memcpy(ptkb.MAC, packet_rx->dest, sizeof(ptkb.MAC));
      else
      {
        memcpy(ptkb.MAC[0], packet_rx->src, sizeof(packet_rx->src));
        memcpy(ptkb.MAC[1], packet_rx->dest, sizeof(packet_rx->dest));
      }
      // 较小的随机数在前, 较大的在后
      if (memcmp(packet_rx->key_nonce, wifi_snonce, sizeof(wifi_snonce)) < 0)
      {
        memcpy(ptkb.nonce[0], packet_rx->key_nonce, sizeof(packet_rx->key_nonce));
        memcpy(ptkb.nonce[1], wifi_snonce, sizeof(wifi_snonce));
      }
      else
      {
        memcpy(ptkb.nonce[0], wifi_snonce, sizeof(wifi_snonce));
        memcpy(ptkb.nonce[1], packet_rx->key_nonce, sizeof(packet_rx->key_nonce));
      }
      // wifi_psk是在设置密码时生成的
      ret = PRF(wifi_psk, sizeof(wifi_psk), "Pairwise key expansion", &ptkb, sizeof(ptkb), &wifi_ptk, sizeof(wifi_ptk)); // PRF-512
      if (!ret)
      {
        printf("PRF: out of memory!\n");
        break;
      }
      //dump_data(&ptk, sizeof(ptk));
      
      /* 发送Message 2: EAPOL-Key(0,1,0,0,P,0,...) */
      WiFi_SendEAPOLResponse(packet_rx, 0x108 | key_type, WiFi_EAPOLProcess_Callback, (void *)2);
      break;
    case 0x1c8: // WPA MSG3
    case 0x3c8: // WPA2 MSG3
      /* 4-way handshake Message 3: EAPOL-Key(1,1,1,1,P,0,...) */
      printf("Message 3 received!\n");
      if (!WiFi_CheckMIC(packet_rx, data->rx_packet_length)) // prevents undetected modification of message 3
      {
        printf("Message 3 is corrupted!\n");
        break;
      }
      if (WiFi_IsCommandBusy()) // 如果命令发送缓冲区被占用, 则丢弃本次的Msg3不作出回应, 等待下一个Msg3
        break;
      
      if (packet_rx->version == 1)
      {
        // WPA认证时, 只将PTK发给固件
        // 必须要在此时发送, 否则没有PTK的话就收不到接下来的Group Key Handshake帧
        WiFi_SetKeyMaterial(key_type, 1, WiFi_EAPOLProcess_Callback, (void *)3);
      }
      else if (packet_rx->version == 2)
      {
        // WPA2认证时, 获取GTK并将PTK和GTK发送给固件
        if (!WiFi_ExtractGTK(packet_rx))
          break;
        WiFi_SetKeyMaterial(key_type, 2, WiFi_EAPOLProcess_Callback, (void *)2);
      }
      
      /* 发送Message 4: EAPOL-Key(1,1,0,0,P,0,...) */
      WiFi_SendEAPOLResponse(packet_rx, 0x308 | key_type, WiFi_EAPOLProcess_Callback, (void *)4);
      break;
    case 0x380:
      /* Group key handshake Message 1: EAPOL-Key(1,1,1,0,G,0,...), G=0 */
      printf("Group key handshake!\n");
      if (!WiFi_CheckMIC(packet_rx, data->rx_packet_length))
        break;
      if (WiFi_IsCommandBusy())
        break;
      
      /* 将新的GTK发给固件 */
      if (!WiFi_ExtractGTK(packet_rx))
        break;
      WiFi_SetKeyMaterial(key_type, 2, WiFi_EAPOLProcess_Callback, (void *)1); // 同时发送PTK和GTK, 不能只发GTK, 否则固件中的密钥无法得到更新
      
      /* 发送Message 2: EAPOL-Key(1,1,0,0,G,0,...) */
      WiFi_SendEAPOLResponse(packet_rx, 0x300 | key_type, WiFi_EAPOLProcess_Callback, (void *)2);
      break;
    default:
      printf("Unhandled EAPOL frame! key_info=0x%04x\n", key_info);
      dump_data(packet_rx, data->rx_packet_length);
  }
}

static void WiFi_EAPOLProcess_Callback(void *arg, void *data, WiFi_Status status)
{
  WiFi_SDIOFrameHeader *header = (WiFi_SDIOFrameHeader *)data;
  if (status == WIFI_STATUS_OK)
  {
    if (header->type == WIFI_SDIOFRAME_COMMAND)
    {
      switch ((uint32_t)arg)
      {
        case 1:
          printf("GTK set!\n");
          break;
        case 2:
          printf("PTK & GTK set!\n");
          break;
        case 3:
          printf("PTK set!\n");
          break;
      }
    }
    else if (header->type == WIFI_SDIOFRAME_DATA)
    {
      printf("Message %d sent!\n", (uint32_t)arg);
      if ((uint32_t)arg == 4)
        WiFi_AuthenticationCompleteHandler(); // 有了PTK就可以发送广播帧了, 所以在这里调用callback比较合适
    }
  }
}

/* 用KEK密钥对key_data数据解密, 并提取出GTK密钥 */
static uint8_t WiFi_ExtractGTK(const WiFi_EAPOLKeyFrame *packet_rx)
{
  uint16_t key_info = ntohs(packet_rx->key_information);
  uint16_t key_len = ntohs(packet_rx->key_length);
  uint16_t keydata_len = ntohs(packet_rx->key_data_length);
  WiFi_KDE *kde;
  WiFi_KeyType key_type = (WiFi_KeyType)(key_info & 0x07);
  
  // 如果命令发送缓冲区被占用, 则丢弃并等待下一个Message
  if (WiFi_IsCommandBusy())
    return 0;
  
  // 解密key_data字段
  kde = (WiFi_KDE *)wifi_tx_command.buffer;
  if (key_type == WIFI_KEYTYPE_TKIP)
    ARC4_decrypt_keydata(wifi_ptk.KEK, packet_rx->key_iv, packet_rx->key_data, keydata_len, wifi_tx_command.buffer);
  else if (key_type == WIFI_KEYTYPE_AES)
    keydata_len = AES_unwrap(wifi_ptk.KEK, packet_rx->key_data, keydata_len, wifi_tx_command.buffer);
  else
    return 0;
  
  if (packet_rx->version == 1)
  {
    // 如果认证类型为WPA, 则解密之后的keydata内容就是GTK
    memcpy(&wifi_gtk, wifi_tx_command.buffer, keydata_len);
    return 1;
  }
  else if (packet_rx->version == 2)
  {
    // 如果认证类型为WPA2, 则解密后的keydata内容是一些KDE结构的数据, GTK在其中的一个KDE里面
    while (kde->length != 0) // 搜索长度不为0的KDE结构
    {
      if (kde->type == 0xdd && kde->data_type == 1 && kde->length - 6 == key_len) // GTK KDE
      {
        memcpy(wifi_gtk.TK, kde->data + 2, key_len);
        return 1;
      }
      kde = (WiFi_KDE *)((uint8_t *)kde + kde->length + 2);
      
      if (((uint8_t *)kde - wifi_tx_command.buffer) >= keydata_len - 1) // 保证key->length落在有效数据区域内
        break;
    }
  }
  
  printf("No GTK KDE!\n");
  return 0;
}

/* 获取MAC地址 */
// callback不能为NULL
void WiFi_GetMACAddress(WiFi_Callback callback, void *arg)
{
  void **p;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  p = malloc(2 * sizeof(void *));
  if (p == NULL)
  {
    printf("WiFi_GetMACAddress: malloc failed!\n");
    callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  
  WiFi_MACAddr(NULL, WIFI_ACT_GET, WiFi_GetMACAddress_Callback, p);
}

static void WiFi_GetMACAddress_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  WiFi_Cmd_MACAddr *cmd = (WiFi_Cmd_MACAddr *)data;
  free(arg);
  
  if (status == WIFI_STATUS_OK)
    app_callback(app_arg, cmd->mac_addr, status);
  else
  {
    printf("WiFi_GetMACAddress error!\n");
    app_callback(app_arg, NULL, status);
  }
}

/* 请求发送新的数据帧 */
uint8_t *WiFi_GetPacketBuffer(void)
{
  WiFi_DataTx *data = (WiFi_DataTx *)wifi_tx_packet.buffer;
  WiFi_WaitForLastTask(); // 使用前必须确保缓冲区未被占用
  return data->payload;
}

/* 获取收到的数据帧大小 */
uint16_t WiFi_GetPacketLength(void)
{
  // 读Scratch pad 4寄存器的低16位
  return WiFi_LowLevel_ReadReg(1, WIFI_SCRATCHPAD4_0) | (WiFi_LowLevel_ReadReg(1, WIFI_SCRATCHPAD4_1) << 8);
}

/* 获取收到的数据帧的内容和大小 */
const uint8_t *WiFi_GetReceivedPacket(uint16_t *len)
{
  WiFi_DataRx *data = (WiFi_DataRx *)wifi_rx;
  if (data->header.type == WIFI_SDIOFRAME_DATA)
  {
    *len = data->rx_packet_length;
    return data->payload;
  }
  else
    return NULL;
}

/* 获取热点的认证类型 */
WiFi_SecurityType WiFi_GetSecurityType(const WiFi_SSIDInfo *info)
{
  if (info->cap_info & WIFI_CAPABILITY_PRIVACY)
  {
    if (info->rsn.header.type)
      return WIFI_SECURITYTYPE_WPA2;
    else if (info->wpa.header.type)
      return WIFI_SECURITYTYPE_WPA;
    else
      return WIFI_SECURITYTYPE_WEP;
  }
  else
    return WIFI_SECURITYTYPE_NONE;
}

/* 初始化WiFi模块 */
void WiFi_Init(void)
{
  // 初始化底层寄存器
  WiFi_LowLevel_Init();
  WiFi_ShowCIS();
  
  // 初始化Function 1
  WiFi_LowLevel_WriteReg(0, SDIO_CCCR_IOEN, SDIO_CCCR_IOEN_IOE1); // IOE1=1 (Enable Function)
  while ((WiFi_LowLevel_ReadReg(0, SDIO_CCCR_IORDY) & SDIO_CCCR_IORDY_IOR1) == 0); // 等待IOR1=1 (I/O Function Ready)
  WiFi_LowLevel_WriteReg(0, SDIO_CCCR_INTEN, SDIO_CCCR_INTEN_IENM | SDIO_CCCR_INTEN_IEN1); // 打开SDIO中断请求
  WiFi_LowLevel_WriteReg(1, WIFI_INTMASK, WIFI_INTMASK_HOSTINTMASK); // 利用中断标志位来判定是否有数据要读取, 可靠性更高
  
  // 下载固件
  wifi_port = WiFi_LowLevel_ReadReg(1, WIFI_IOPORT0) | (WiFi_LowLevel_ReadReg(1, WIFI_IOPORT1) << 8) | (WiFi_LowLevel_ReadReg(1, WIFI_IOPORT2) << 16);
  WiFi_LowLevel_SetBlockSize(1, 32);
  WiFi_DownloadFirmware();
  WiFi_LowLevel_SetBlockSize(1, 256);
}

void WiFi_Input(void)
{
  uint8_t status;
  uint16_t len;
  WiFi_SDIOFrameHeader *rx_header = (WiFi_SDIOFrameHeader *)wifi_rx;
  WiFi_DataRx *rx_frame = (WiFi_DataRx *)wifi_rx;
  WiFi_CommandHeader *rx_cmd = (WiFi_CommandHeader *)wifi_rx;
#ifdef WIFI_DISPLAY_RESPTIME
  WiFi_CommandHeader *tx_cmd = (WiFi_CommandHeader *)wifi_tx_command.buffer;
#endif
  
  status = WiFi_LowLevel_ReadReg(1, WIFI_INTSTATUS); // 获取需要处理的中断标志位
  WiFi_LowLevel_WriteReg(1, WIFI_INTSTATUS, WIFI_INTSTATUS_ALL & ~status); // 必须先清除这些标志位, 然后再进行处理, 这样可以避免清除掉处理过程中新来的中断
  
  if (status & WIFI_INTSTATUS_DNLD)
  {
    // 命令帧收到确认
    if (wifi_tx_command.busy && wifi_tx_command.ready == 0)
    {
#ifdef WIFI_DISPLAY_RESPTIME
      printf("CMD 0x%04x ACK at %dms\n", tx_cmd->cmd_code, sys_now() - wifi_tx_command.start_time);
#endif
      wifi_tx_command.ready = 1;
    }
    
    // 数据帧发送成功并收到确认
    if (wifi_tx_packet.busy)
    {
#ifdef WIFI_DISPLAY_RESPTIME
      printf("Packet ACK at %dms\n", sys_now() - wifi_tx_packet.start_time);
#endif
      WiFi_TxBufferComplete(&wifi_tx_packet, wifi_tx_packet.buffer, WIFI_STATUS_OK);
    }
  }
  
  if (status & WIFI_INTSTATUS_UPLD)
  {
    len = WiFi_GetPacketLength();
    WiFi_LowLevel_ReadData(1, wifi_port, wifi_rx, len, sizeof(wifi_rx));
    switch (rx_header->type)
    {
      case WIFI_SDIOFRAME_DATA:
        // 收到以太网数据帧
        if (rx_frame->rx_packet_length >= 14 && rx_frame->payload[12] == 0x88 && rx_frame->payload[13] == 0x8e)
          WiFi_EAPOLProcess(rx_frame); // 处理0x888e类型的EAPOL认证帧
        else
          WiFi_PacketHandler((WiFi_DataRx *)wifi_rx);
        break;
      case WIFI_SDIOFRAME_COMMAND:
        // 收到命令回应帧
        if (((WiFi_CommandHeader *)wifi_rx)->seq_num == rx_cmd->seq_num) // 序号相符
        {
#ifdef WIFI_DISPLAY_RESPTIME
          printf("CMDRESP 0x%04x at %dms\n", rx_cmd->cmd_code, sys_now() - wifi_tx_command.start_time);
#endif
          WiFi_TxBufferComplete(&wifi_tx_command, wifi_rx, WIFI_STATUS_OK);
        }
        break;
      case WIFI_SDIOFRAME_EVENT:
        // 收到事件帧
        WiFi_EventHandler((WiFi_Event *)wifi_rx); // 调用事件处理回调函数
    }
  }
}

/* 发送命令帧前, 必须保证命令发送缓冲区为空 */
uint8_t WiFi_IsCommandBusy(void)
{
  return wifi_tx_command.busy;
}

/* 加入Ad-Hoc网络 */
// 参数mac_addr用于接收MAC地址, 可以为NULL, 但是不能指向局部变量
void WiFi_JoinADHOC(const char *ssid, uint8_t *mac_addr, WiFi_Callback callback, void *arg)
{
  void **p;
  WiFi_SSIDInfo *info;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  p = malloc(3 * sizeof(void *) + sizeof(WiFi_SSIDInfo));
  if (p == NULL)
  {
    printf("WiFi_JoinADHOC: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = mac_addr;
  info = (WiFi_SSIDInfo *)(p + 3);
  
  WiFi_ScanSSID(ssid, info, WiFi_JoinADHOC_Callback, p);
}

static void WiFi_JoinADHOC_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  uint8_t *mac_addr = (uint8_t *)p[2];
  WiFi_SSIDInfo *info = (WiFi_SSIDInfo *)(p + 3);
  WiFi_Cmd_ADHOCJoin *cmd;
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_JoinADHOC error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  switch (WiFi_GetCommandCode(data))
  {
    case CMD_802_11_SCAN:
      cmd = (WiFi_Cmd_ADHOCJoin *)wifi_tx_command.buffer;
      memcpy(cmd->bssid, info->mac_addr, sizeof(cmd->bssid));
      memset(cmd->ssid, 0, sizeof(cmd->ssid));
      strncpy((char *)cmd->ssid, (char *)info->ssid.ssid, sizeof(cmd->ssid));
      cmd->bss_type = WIFI_BSS_ANY; // recommended for use when joining Ad-Hoc networks
      cmd->bcn_period = info->bcn_period;
      cmd->dtim_period = 1;
      memset(cmd->timestamp, 0, sizeof(cmd->timestamp) + sizeof(cmd->start_ts));
      cmd->ds_param_set.header.type = WIFI_MRVLIETYPES_DSPARAMSET;
      cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set);
      cmd->ds_param_set.channel = info->channel;
      cmd->reserved1 = 0;
      cmd->ibss_param_set.header.type = WIFI_MRVLIETYPES_IBSSPARAMSET;
      cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set);
      cmd->ibss_param_set.atim_window = 0;
      cmd->reserved2 = 0;
      cmd->cap_info = info->cap_info;
      memcpy(cmd->data_rates, info->rates.rates, sizeof(cmd->data_rates));
      cmd->reserved3 = 0;
      WiFi_SendCommand(CMD_802_11_AD_HOC_JOIN, wifi_tx_command.buffer, sizeof(WiFi_Cmd_ADHOCJoin), WiFi_JoinADHOC_Callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
      break;
    case CMD_802_11_AD_HOC_JOIN:
      if (mac_addr != NULL)
        memcpy(mac_addr, info->mac_addr, 6);
      free(arg);
      cmd = (WiFi_Cmd_ADHOCJoin *)data;
      if (app_callback)
        app_callback(app_arg, data, (cmd->header.result == 0) ? WIFI_STATUS_OK : WIFI_STATUS_FAIL);
      break;
  }
}

/* 加入带有密码的Ad-Hoc网络 */
void WiFi_JoinADHOCEx(const WiFi_Connection *conn, int32_t max_retry, WiFi_Callback callback, void *arg)
{
  uint16_t ssid_len;
  void **p;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  if (conn->security == WIFI_SECURITYTYPE_WPA || conn->security == WIFI_SECURITYTYPE_WPA2)
  {
    printf("WiFi_JoinADHOCEx: WPA is not supported!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_INVALID);
    return;
  }
  
  ssid_len = strlen(conn->ssid);
  p = malloc(4 * sizeof(void *) + ssid_len + 1);
  if (p == NULL)
  {
    printf("WiFi_JoinADHOCEx: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = (void *)max_retry;
  p[3] = conn->mac_addr;
  memcpy(p + 4, conn->ssid, ssid_len + 1);
  
  if (conn->security == WIFI_SECURITYTYPE_WEP)
    WiFi_SetWEP(WIFI_ACT_ADD, conn->password, WiFi_JoinADHOCEx_Callback, p);
  else
    WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_JoinADHOCEx_Callback, p);
}

static void WiFi_JoinADHOCEx_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  int32_t *pmax_retry = (int32_t *)(p + 2);
  uint8_t *mac_addr = (uint8_t *)p[3];
  char *ssid = (char *)(p + 4);
  uint16_t cmd_code = WiFi_GetCommandCode(data);
  
  if (cmd_code == CMD_802_11_AD_HOC_JOIN || cmd_code == CMD_802_11_SCAN)
  {
    if (status == WIFI_STATUS_OK)
    {
      free(arg);
      if (app_callback)
        app_callback(app_arg, data, status);
      return;
    }
    else
    {
      if (*pmax_retry != 0)
      {
        if (*pmax_retry != -1)
          (*pmax_retry)--;
        cmd_code = CMD_MAC_CONTROL;
        status = WIFI_STATUS_OK;
      }
    }
  }
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_JoinADHOCEx error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  switch (cmd_code)
  {
    case CMD_802_11_SET_WEP:
      WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_WEP | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_JoinADHOCEx_Callback, arg);
      break;
    case CMD_MAC_CONTROL:
      WiFi_JoinADHOC(ssid, mac_addr, WiFi_JoinADHOCEx_Callback, arg);
  }
}

/* 获取或设置WPA密钥 */
void WiFi_KeyMaterial(WiFi_CommandAction action, MrvlIETypes_KeyParamSet_t *key, uint8_t key_count, WiFi_Callback callback, void *arg)
{
  uint8_t i;
  WiFi_Cmd_KeyMaterial *cmd = (WiFi_Cmd_KeyMaterial *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  cmd->action = action;
  cmd++;
  if (action == WIFI_ACT_SET)
  {
    for (i = 0; i < key_count; i++)
    {
      key[i].header.type = WIFI_MRVLIETYPES_KEYPARAMSET;
      key[i].header.length = sizeof(MrvlIETypes_KeyParamSet_t) - sizeof(key[i].header) - sizeof(key[i].key) + key->key_len;
      memcpy(cmd, key + i, TLV_STRUCTLEN(key[i]));
      cmd = (WiFi_Cmd_KeyMaterial *)TLV_NEXT((MrvlIETypes_KeyParamSet_t *)cmd);
    }
  }
  WiFi_SendCommand(CMD_802_11_KEY_MATERIAL, wifi_tx_command.buffer, (uint8_t *)cmd - wifi_tx_command.buffer, callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 获取或设置MAC地址 */
void WiFi_MACAddr(const uint8_t newaddr[6], WiFi_CommandAction action, WiFi_Callback callback, void *arg)
{
  WiFi_Cmd_MACAddr *cmd = (WiFi_Cmd_MACAddr *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  cmd->action = action;
  if (action == WIFI_ACT_SET)
    memcpy(cmd->mac_addr, newaddr, 6);
  WiFi_SendCommand(CMD_802_11_MAC_ADDR, wifi_tx_command.buffer, sizeof(WiFi_Cmd_MACAddr), callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 配置MAC */
void WiFi_MACControl(uint16_t action, WiFi_Callback callback, void *arg)
{
  WiFi_Cmd_MACCtrl *cmd = (WiFi_Cmd_MACCtrl *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  cmd->action = action;
  cmd->reserved = 0;
  WiFi_SendCommand(CMD_MAC_CONTROL, wifi_tx_command.buffer, sizeof(WiFi_Cmd_MACCtrl), callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 扫描全部热点 (仅显示) */
void WiFi_Scan(WiFi_Callback callback, void *arg)
{
  uint8_t i;
  void **p;
  WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)wifi_tx_command.buffer; // 要发送的命令
  MrvlIETypes_ChanListParamSet_t *chanlist = (MrvlIETypes_ChanListParamSet_t *)(cmd + 1); // 这里的+1指的是前进sizeof(指针类型)个地址单元, 而非只前进1个地址单元
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  p = malloc(2 * sizeof(void *));
  if (p == NULL)
  {
    printf("WiFi_Scan: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  
  cmd->bss_type = WIFI_BSS_ANY;
  memset(cmd->bss_id, 0, sizeof(cmd->bss_id));
  
  // 通道的基本参数
  chanlist->header.type = WIFI_MRVLIETYPES_CHANLISTPARAMSET;
  chanlist->header.length = 4 * sizeof(chanlist->channels);
  for (i = 0; i < 4; i++) // 先扫描前4个通道
  {
    chanlist->channels[i].band_config_type = 0;
    chanlist->channels[i].chan_number = i + 1; // 通道号
    chanlist->channels[i].scan_type = 0;
    chanlist->channels[i].min_scan_time = 0;
    chanlist->channels[i].max_scan_time = 100;
  }
  
  WiFi_SendCommand(CMD_802_11_SCAN, wifi_tx_command.buffer, sizeof(WiFi_CmdRequest_Scan) + TLV_STRUCTLEN(*chanlist), WiFi_Scan_Callback, p, 3000, WIFI_DEFAULT_MAXRETRY);
}

static void WiFi_Scan_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  
  uint8_t i, j, n;
  WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)wifi_tx_command.buffer;
  MrvlIETypes_ChanListParamSet_t *chanlist = (MrvlIETypes_ChanListParamSet_t *)(cmd + 1);
  
  uint8_t ssid[33], channel;
  uint16_t ie_size;
  WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)data;
  WiFi_BssDescSet *bss_desc_set;
  WiFi_SecurityType security;
  WiFi_Vendor *vendor;
  IEEEType *ie_params;
  //MrvlIETypes_TsfTimestamp_t *tft_table;
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_Scan error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  // 发送扫描接下来的4个通道的命令
  j = chanlist->channels[0].chan_number + 4;
  if (j < 14)
  {
    if (j == 13)
      n = 2;
    else
      n = 4;
    
    chanlist->header.length = n * sizeof(chanlist->channels);
    for (i = 0; i < n; i++)
      chanlist->channels[i].chan_number = i + j;
    WiFi_SendCommand(CMD_802_11_SCAN, wifi_tx_command.buffer, sizeof(WiFi_CmdRequest_Scan) + TLV_STRUCTLEN(*chanlist), WiFi_Scan_Callback, arg, 3000, WIFI_DEFAULT_MAXRETRY);
  }
  else
    n = 0;
  
  // 显示本次扫描结果, num_of_set为热点数
  if (resp->num_of_set > 0)
  {
    bss_desc_set = (WiFi_BssDescSet *)(resp + 1);
    for (i = 0; i < resp->num_of_set; i++)
    {
      security = WIFI_SECURITYTYPE_WEP;
      ie_params = &bss_desc_set->ie_parameters;
      ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters));
      while (ie_size > 0)
      {
        switch (ie_params->header.type)
        {
          case WIFI_MRVLIETYPES_SSIDPARAMSET:
            // SSID名称
            memcpy(ssid, ie_params->data, ie_params->header.length);
            ssid[ie_params->header.length] = '\0';
            break;
          case WIFI_MRVLIETYPES_DSPARAMSET:
            // 通道号
            channel = ie_params->data[0];
            break;
          case WIFI_MRVLIETYPES_RSNPARAMSET:
            security = WIFI_SECURITYTYPE_WPA2;
            break;
          case WIFI_MRVLIETYPES_VENDORPARAMSET:
            if (security != WIFI_SECURITYTYPE_WPA2)
            {
              vendor = (WiFi_Vendor *)ie_params->data;
              if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2 && vendor->oui_type == 0x01)
                security = WIFI_SECURITYTYPE_WPA;
            }
            break;
        }
        ie_size -= TLV_STRUCTLEN(*ie_params);
        ie_params = (IEEEType *)TLV_NEXT(ie_params);
      }
      if ((bss_desc_set->cap_info & WIFI_CAPABILITY_PRIVACY) == 0)
        security = WIFI_SECURITYTYPE_NONE;
      if (ie_size != 0)
        printf("ie_parameters error!\n");
      
      printf("SSID '%s', ", ssid); // 热点名称
      printf("MAC %02X:%02X:%02X:%02X:%02X:%02X, ", bss_desc_set->bssid[0], bss_desc_set->bssid[1], bss_desc_set->bssid[2], bss_desc_set->bssid[3], bss_desc_set->bssid[4], bss_desc_set->bssid[5]); // MAC地址
      printf("RSSI %d, Channel %d\n", bss_desc_set->rssi, channel); // 信号强度和通道号
      //printf("  Timestamp %lld, Beacon interval %d\n", bss_desc_set->pkt_time_stamp, bss_desc_set->bcn_interval);
      
      printf("  Capability: 0x%04x (Security: ", bss_desc_set->cap_info);
      switch (security)
      {
        case WIFI_SECURITYTYPE_NONE:
          printf("Unsecured");
          break;
        case WIFI_SECURITYTYPE_WEP:
          printf("WEP");
          break;
        case WIFI_SECURITYTYPE_WPA:
          printf("WPA");
          break;
        case WIFI_SECURITYTYPE_WPA2:
          printf("WPA2");
          break;
      }
      
      printf(", Mode: ");
      if (bss_desc_set->cap_info & WIFI_CAPABILITY_IBSS)
        printf("Ad-Hoc");
      else
        printf("Infrastructure");
      printf(")\n");
      
      // 转向下一个热点信息
      bss_desc_set = (WiFi_BssDescSet *)((uint8_t *)bss_desc_set + sizeof(bss_desc_set->ie_length) + bss_desc_set->ie_length);
    }
    
    // resp->buf_size就是bss_desc_set的总大小
    // 因此tft_table == buffer + sizeof(WiFi_CmdResponse_Scan) + resp->buf_size
    /*
    tft_table = (MrvlIETypes_TsfTimestamp_t *)bss_desc_set;
    if (tft_table->header.type == WIFI_MRVLIETYPES_TSFTIMESTAMP && tft_table->header.length == resp->num_of_set * sizeof(uint64_t))
    {
      printf("Timestamps: ");
      for (i = 0; i < resp->num_of_set; i++)
        printf("%lld ", tft_table->tsf_table[i]);
      printf("\n");
    }
    */
    
    // TSF timestamp table是整个数据的末尾, 后面没有Channel/band table
    //if (((uint8_t *)tft_table - (uint8_t *)data) + TLV_STRUCTLEN(*tft_table) == resp->header.frame_header.length)
    //  printf("data end!\n");
  }
  
  // 扫描完毕时调用回调函数
  if (n == 0)
  {
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
  }
}

/* 获取指定名称的热点的详细信息 */
void WiFi_ScanSSID(const char *ssid, WiFi_SSIDInfo *info, WiFi_Callback callback, void *arg)
{
  uint8_t i;
  void **p;
  MrvlIETypes_ChanListParamSet_t *chan_list;
  WiFi_CmdRequest_Scan *cmd = (WiFi_CmdRequest_Scan *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  p = malloc(3 * sizeof(void *));
  if (p == NULL)
  {
    printf("WiFi_ScanSSID: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = info;
  memset(info, 0, sizeof(WiFi_SSIDInfo)); // 将整个info结构体清零
  
  cmd->bss_type = WIFI_BSS_ANY;
  memset(cmd->bss_id, 0, sizeof(cmd->bss_id));
  
  // 给info->ssid成员赋值
  info->ssid.header.type = WIFI_MRVLIETYPES_SSIDPARAMSET;
  info->ssid.header.length = strlen(ssid);
  memcpy(info->ssid.ssid, ssid, info->ssid.header.length);
  memcpy(cmd + 1, &info->ssid, TLV_STRUCTLEN(info->ssid)); // 把info->ssid复制到待发送的命令内容中
  
  chan_list = (MrvlIETypes_ChanListParamSet_t *)((uint8_t *)(cmd + 1) + TLV_STRUCTLEN(info->ssid));
  chan_list->header.type = WIFI_MRVLIETYPES_CHANLISTPARAMSET;
  chan_list->header.length = 14 * sizeof(chan_list->channels); // 一次性扫描14个通道
  for (i = 0; i < 14; i++)
  {
    chan_list->channels[i].band_config_type = 0;
    chan_list->channels[i].chan_number = i + 1;
    chan_list->channels[i].scan_type = 0;
    chan_list->channels[i].min_scan_time = 0;
    chan_list->channels[i].max_scan_time = 100;
  }
  
  WiFi_SendCommand(CMD_802_11_SCAN, wifi_tx_command.buffer, ((uint8_t *)chan_list - wifi_tx_command.buffer) + TLV_STRUCTLEN(*chan_list), WiFi_ScanSSID_Callback, p, 3000, WIFI_DEFAULT_MAXRETRY);
}

static void WiFi_ScanSSID_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  WiFi_SSIDInfo *info = p[2];
  
  uint16_t ie_size;
  WiFi_CmdResponse_Scan *resp = (WiFi_CmdResponse_Scan *)data;
  WiFi_BssDescSet *bss_desc_set = (WiFi_BssDescSet *)(resp + 1);
  IEEEType *ie_params;
  WiFi_Vendor *vendor;
  
  free(arg);
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_ScanSSID error!\n");
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  if (resp->num_of_set == 0)
  {
    // 未找到指定的AP热点, 此时info结构体中除了ssid成员外, 其余的成员均为0
    // resp中的内容到了num_of_set成员后就没有了
    printf("No SSID!\n");
    if (app_callback)
      app_callback(app_arg, data, WIFI_STATUS_NOTFOUND);
    return;
  }
  
  // bss_desc_set以扫描到的第一个信息项为准
  memcpy(info->mac_addr, bss_desc_set->bssid, sizeof(info->mac_addr));
  info->cap_info = bss_desc_set->cap_info;
  info->bcn_period = bss_desc_set->bcn_interval;
  
  // 若info->xxx.header.type=0, 则表明没有该项的信息 (除SSID结构体外, 因为SSID的type=WIFI_MRVLIETYPES_SSIDPARAMSET=0)
  ie_params = &bss_desc_set->ie_parameters;
  ie_size = bss_desc_set->ie_length - (sizeof(WiFi_BssDescSet) - sizeof(bss_desc_set->ie_length) - sizeof(bss_desc_set->ie_parameters)); // 所有IEEE_Type数据的总大小
  while (ie_size > 0)
  {
    switch (ie_params->header.type)
    {
      case WIFI_MRVLIETYPES_RATESPARAMSET:
        // 速率
        WiFi_TranslateTLV((MrvlIEType *)&info->rates, ie_params, sizeof(info->rates.rates));
        break;
      case WIFI_MRVLIETYPES_DSPARAMSET:
        // 通道号
        info->channel = ie_params->data[0];
        break;
      case WIFI_MRVLIETYPES_RSNPARAMSET:
        // 通常只有一个RSN信息 (与WPA2相关)
        // printf("RSN len=%d\n", ie_params->length);
        WiFi_TranslateTLV((MrvlIEType *)&info->rsn, ie_params, sizeof(info->rsn.rsn));
        break;
      case WIFI_MRVLIETYPES_VENDORPARAMSET:
        // 通常会有多项VENDOR信息 (与WPA相关)
        vendor = (WiFi_Vendor *)ie_params->data;
        if (vendor->oui[0] == 0x00 && vendor->oui[1] == 0x50 && vendor->oui[2] == 0xf2)
        {
          switch (vendor->oui_type)
          {
            case 0x01:
              // wpa_oui
              WiFi_TranslateTLV((MrvlIEType *)&info->wpa, ie_params, sizeof(info->wpa.vendor));
              break;
            case 0x02:
              // wmm_oui
              if (ie_params->header.length == 24) // 合法大小
                WiFi_TranslateTLV((MrvlIEType *)&info->wwm, ie_params, sizeof(info->wwm.vendor));
              break;
            case 0x04:
              // wps_oui
              WiFi_TranslateTLV((MrvlIEType *)&info->wps, ie_params, sizeof(info->wps.vendor));
              break;
          }
        }
        break;
    }
    
    // 转向下一个TLV
    ie_size -= TLV_STRUCTLEN(*ie_params);
    ie_params = (IEEEType *)TLV_NEXT(ie_params);
  }
  
  if (app_callback)
    app_callback(app_arg, data, status);
}

/* 发送WiFi命令, 收到回应或超时时调用callback回调函数 */
// size=0表示data包含完整的命令数据, 且code和size可直接从data中获取(用于重发)
// retry可以为0(第一次失败时就直接调用回调函数, 不再重试), 但timeout不能为0(否则收到回应前会误认为超时并调用回调函数)
//
// 无操作系统的环境下只能使用非阻塞方式执行WiFi命令, 并通过回调函数通知命令执行的结果 (回调函数应保证能够被调用并只调用一次)
// 如果有操作系统, 某个任务想要以阻塞方式执行WiFi命令, 可以在该函数里面添加发送命令前阻塞等待表示命令通道是否可用的0-1信号量的代码
// 当命令通道可用时, 使信号量的值为1并唤醒其中一个等待发送命令的任务, 发送命令后继续阻塞等待回应, 调用回调函数并根据命令执行结果(成功还是失败)决定函数的返回值
void WiFi_SendCommand(uint16_t code, const void *data, uint16_t size, WiFi_Callback callback, void *arg, uint32_t timeout, uint8_t max_retry)
{
  static uint16_t seq_num = 0;
  WiFi_CommandHeader *cmdhdr = (WiFi_CommandHeader *)wifi_tx_command.buffer;
  
  if (WiFi_CheckCommandBusy(callback, arg)) // 发送命令前必须确保之前的命令已经发送完毕
    return;
  
  if (data != wifi_tx_command.buffer)
    memcpy(wifi_tx_command.buffer, data, (size != 0) ? size : cmdhdr->frame_header.length); // 将要发送的命令内容复制到缓冲区中, 以便出错时重发
  
  if (size != 0)
  {
    cmdhdr->frame_header.length = size;
    cmdhdr->frame_header.type = WIFI_SDIOFRAME_COMMAND;
    cmdhdr->cmd_code = code;
    cmdhdr->size = size - sizeof(WiFi_SDIOFrameHeader); // 命令大小包括命令头部, 但不包括SDIO帧头部
    cmdhdr->seq_num = seq_num++;
    cmdhdr->result = 0;
  }
  else
    size = cmdhdr->frame_header.length; // 重发命令时不填写cmdhdr
  WiFi_LowLevel_WriteData(1, wifi_port, wifi_tx_command.buffer, size, sizeof(wifi_tx_command.buffer));
  // WriteData函数出错的概率很小, 这里简单起见就不去判断它的返回值了
  // 即使出错了(如CRC校验错误), 由于收不到命令回应, WiFi_CheckTimeout函数也会重传该命令
  
  wifi_tx_command.arg = arg;
  wifi_tx_command.busy = 1;
  wifi_tx_command.callback = callback;
  wifi_tx_command.ready = 0;
  wifi_tx_command.retry = max_retry;
  wifi_tx_command.start_time = sys_now();
  wifi_tx_command.timeout = timeout;
}

/* 发送EAPOL回应帧 */
static void WiFi_SendEAPOLResponse(const WiFi_EAPOLKeyFrame *packet_rx, uint16_t key_info, WiFi_Callback callback, void *arg)
{
  uint8_t ret;
  uint16_t len;
  WiFi_EAPOLKeyFrame *packet_tx = (WiFi_EAPOLKeyFrame *)WiFi_GetPacketBuffer();
  WiFi_KeyType key_type = (WiFi_KeyType)(key_info & 0x07);
  WiFi_MIC mic;
  
  memcpy(packet_tx->dest, packet_rx->src, 6); // 目标MAC地址
  memcpy(packet_tx->src, packet_rx->dest, 6); // 源MAC地址
  packet_tx->type = htons(0x888e); // 大端序的0x888e: 802.1X Authentication
  packet_tx->version = packet_rx->version; // WPA版本(1或2) (MIC从此字段开始计算)
  packet_tx->packet_type = packet_rx->packet_type; // 通常为3: Key
  packet_tx->packet_body_length = packet_tx->key_data - &packet_tx->descriptor_type; // 不含最后的变长数据
  packet_tx->packet_body_length = htons(packet_tx->packet_body_length);
  
  packet_tx->descriptor_type = packet_rx->descriptor_type; // WPA为254, WPA2为2
  packet_tx->key_information = htons(key_info);
  packet_tx->key_length = packet_rx->key_length;
  memcpy(packet_tx->key_replay_counter, packet_rx->key_replay_counter, sizeof(packet_rx->key_replay_counter));
  memcpy(packet_tx->key_nonce, wifi_snonce, sizeof(wifi_snonce));
  memset(packet_tx->key_iv, 0, sizeof(packet_tx->key_iv) + sizeof(packet_tx->key_rsc) + sizeof(packet_tx->reserved));
  packet_tx->key_data_length = 0;
  
  // 用KCK对要发送的EAPOL帧(以太网帧去掉两个MAC地址字段和type/length=0x888e字段剩下的payload)进行运算得到MIC
  len = sizeof(WiFi_EAPOLKeyFrame) - sizeof(packet_tx->key_data);
  memset(packet_tx->key_mic, 0, sizeof(packet_tx->key_mic)); // 先将MIC字段清零
  if (key_type == WIFI_KEYTYPE_TKIP)
    ret = hmac_md5(wifi_ptk.KCK, sizeof(wifi_ptk.KCK), &packet_tx->version, len - 14, mic.MIC); // 计算MIC
  else if (key_type == WIFI_KEYTYPE_AES)
    ret = hmac_sha1(wifi_ptk.KCK, sizeof(wifi_ptk.KCK), &packet_tx->version, len - 14, mic.MIC);
  else
  {
    printf("WiFi_SendEAPOLResponse: unsupported key type!\n");
    if (callback)
      callback(arg, packet_tx, WIFI_STATUS_INVALID);
    return;
  }
  if (!ret)
  {
    printf("WiFi_SendEAPOLResponse: out of memory!\n");
    if (callback)
      callback(arg, packet_tx, WIFI_STATUS_MEM);
    return;
  }
  
  memcpy(packet_tx->key_mic, mic.MIC, sizeof(mic.MIC)); // 将计算结果放到MIC字段上
  WiFi_SendPacket(packet_tx, len, callback, arg, WIFI_DEFAULT_TIMEOUT_DATAACK, 0); // 因为路由器接收回应超时时会更换新的密钥, 所以EAPOL回应帧发送失败后一律不重传
}

/* 发送数据帧 */
// data指向的是WiFi_DataTx.payload
// size=0时表明发送缓冲区中包含了完整的数据帧封包, 此时data参数将被忽略 (用于重发)
void WiFi_SendPacket(void *data, uint16_t size, WiFi_Callback callback, void *arg, uint32_t timeout, uint8_t max_retry)
{
  WiFi_DataTx *packet = (WiFi_DataTx *)wifi_tx_packet.buffer;
  
  if (size == 0)
    data = packet->payload;
  
  WiFi_WaitForLastTask();
  if (data != packet->payload)
    memcpy(packet->payload, data, size); // 将要发送的数据内容复制到缓冲区中, 以便出错时重发
 
  if (size != 0)
  {
    // 有关发送数据包的细节, 请参考Firmware Specification PDF的Chapter 3: Data Path
    packet->header.length = sizeof(WiFi_DataTx) - sizeof(packet->payload) + size;
    packet->header.type = WIFI_SDIOFRAME_DATA;
    
    packet->reserved1 = 0;
    packet->tx_control = 0; // 控制信息的格式请参考3.2.1 Per-Packet Settings
    packet->tx_packet_offset = sizeof(WiFi_DataTx) - sizeof(packet->payload) - sizeof(packet->header); // 不包括SDIOFrameHeader
    packet->tx_packet_length = size;
    memcpy((void *)&packet->tx_dest_addr_high, packet->payload, 6);
    packet->priority = 0;
    packet->flags = 0;
    packet->pkt_delay_2ms = 0;
    packet->reserved2 = 0;
  }
  WiFi_LowLevel_WriteData(1, wifi_port, wifi_tx_packet.buffer, packet->header.length, sizeof(wifi_tx_packet.buffer));
  
  // 接下来需要等待Download Ready位置1, 表明数据帧发送成功
  wifi_tx_packet.arg = arg;
  wifi_tx_packet.busy = 1;
  wifi_tx_packet.callback = callback;
  wifi_tx_packet.retry = max_retry;
  wifi_tx_packet.start_time = sys_now();
  wifi_tx_packet.timeout = timeout;
}

/* 将PTK和GTK密钥发送给固件使用 */
static void WiFi_SetKeyMaterial(WiFi_KeyType key_type, uint8_t key_num, WiFi_Callback callback, void *arg)
{
  MrvlIETypes_KeyParamSet_t keys[2];
  uint16_t key_len;
  if (key_type == WIFI_KEYTYPE_TKIP)
    key_len = sizeof(wifi_ptk.TK) + sizeof(wifi_ptk.TKIPTxMICKey) + sizeof(wifi_ptk.TKIPRxMICKey);
  else if (key_type == WIFI_KEYTYPE_AES)
    key_len = sizeof(wifi_ptk.TK);
  else
  {
    if (callback)
      callback(arg, NULL, WIFI_STATUS_INVALID);
    return;
  }
  
  keys[0].key_type_id = key_type;
  keys[0].key_info = WIFI_KEYINFO_KEYENABLED | WIFI_KEYINFO_UNICASTKEY; // 单播密钥
  keys[0].key_len = key_len;
  memcpy(keys[0].key, wifi_ptk.TK, sizeof(wifi_ptk.TK));
  if (key_type == WIFI_KEYTYPE_TKIP)
  {
    // 固件中表示的MIC校验用的密钥顺序刚好和PRF函数产生的顺序相反
    memcpy(keys[0].key + sizeof(wifi_ptk.TK), wifi_ptk.TKIPRxMICKey, sizeof(wifi_ptk.TKIPRxMICKey));
    memcpy(keys[0].key + sizeof(wifi_ptk.TK) + sizeof(wifi_ptk.TKIPRxMICKey), wifi_ptk.TKIPTxMICKey, sizeof(wifi_ptk.TKIPTxMICKey));
  }
  
  if (key_num == 2)
  {
    keys[1].key_type_id = key_type;
    keys[1].key_info = WIFI_KEYINFO_KEYENABLED | WIFI_KEYINFO_MULTICASTKEY; // 多播、组播密钥
    keys[1].key_len = key_len;
    memcpy(keys[1].key, wifi_gtk.TK, sizeof(wifi_gtk.TK));
    if (key_type == WIFI_KEYTYPE_TKIP)
    {
      memcpy(keys[1].key + sizeof(wifi_gtk.TK), wifi_gtk.TKIPRxMICKey, sizeof(wifi_gtk.TKIPRxMICKey));
      memcpy(keys[1].key + sizeof(wifi_gtk.TK) + sizeof(wifi_gtk.TKIPRxMICKey), wifi_gtk.TKIPTxMICKey, sizeof(wifi_gtk.TKIPTxMICKey));
    }
  }
  
  WiFi_KeyMaterial(WIFI_ACT_SET, keys, key_num, callback, arg);
}

/* 设置WEP密钥 (长度必须为5或13个字符) */
// action: WIFI_ACT_ADD / WIFI_ACT_REMOVE, 移除密钥时参数key可以设为NULL
void WiFi_SetWEP(WiFi_CommandAction action, const WiFi_WEPKey *key, WiFi_Callback callback, void *arg)
{
  uint8_t i, j, len;
  uint16_t cmd_size;
  uint32_t temp;
  WiFi_Cmd_SetWEP *cmd = (WiFi_Cmd_SetWEP *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  cmd->action = action;
  cmd->tx_key_index = key->index;
  
  memset(cmd->wep_types, 0, sizeof(cmd->wep_types) + sizeof(cmd->keys));
  if (action == WIFI_ACT_ADD)
  {
    for (i = 0; i < 4; i++)
    {
      if (key->keys[i] == NULL)
        continue;
      
      len = strlen(key->keys[i]);
      if (len == 5 || len == 13)
      {
        // 5个或13个ASCII密钥字符
        if (len == 5)
          cmd->wep_types[i] = WIFI_WEPKEYTYPE_40BIT;
        else if (len == 13)
          cmd->wep_types[i] = WIFI_WEPKEYTYPE_104BIT;
        memcpy(cmd->keys[i], key->keys[i], len);
      }
      else if (len == 10 || len == 26)
      {
        // 10个或26个16进制密钥字符
        if (len == 10)
          cmd->wep_types[i] = WIFI_WEPKEYTYPE_40BIT;
        else if (len == 26)
          cmd->wep_types[i] = WIFI_WEPKEYTYPE_104BIT;
        
        for (j = 0; j < len; j++)
        {
          if (!(key->keys[i][j] >= '0' && key->keys[i][j] <= '9') && !(key->keys[i][j] >= 'a' && key->keys[i][j] <= 'f') && !(key->keys[i][j] >= 'A' && key->keys[i][j] <= 'F'))
          {
            printf("WiFi_SetWEP: The hex key %d contains invalid characters!\n", i);
            if (callback)
              callback(arg, NULL, WIFI_STATUS_INVALID);
            return;
          }
          if (j % 2 == 0)
          {
            sscanf(key->keys[i] + j, "%02x", &temp);
            cmd->keys[i][j / 2] = temp;
          }
        }
      }
      else
      {
        printf("WiFi_SetWEP: The length of key %d is invalid!\n", i);
        if (callback)
          callback(arg, NULL, WIFI_STATUS_INVALID);
        return;
      }
    }
    cmd_size = sizeof(WiFi_Cmd_SetWEP);
  }
  else if (action == WIFI_ACT_REMOVE)
    cmd_size = cmd->wep_types - wifi_tx_command.buffer;
  else
  {
    if (callback)
      callback(arg, NULL, WIFI_STATUS_INVALID);
    return;
  }
  WiFi_SendCommand(CMD_802_11_SET_WEP, wifi_tx_command.buffer, cmd_size, callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 设置WPA密码, 并生成PSK */
void WiFi_SetWPA(const char *ssid, const char *password)
{
  uint8_t ret = pbkdf2_hmac_sha1(password, strlen(password), ssid, strlen(ssid), 4096, wifi_psk, sizeof(wifi_psk));
  if (!ret)
    printf("WiFi_SetWPA: out of memory!\n");
}

/* 显示WiFi模块信息 */
void WiFi_ShowCIS(void)
{
  uint8_t data[255];
  uint8_t func, i, n, len;
  uint8_t tpl_code, tpl_link; // 16.2 Basic Tuple Format and Tuple Chain Structure
  uint32_t cis_ptr;
  
  n = WiFi_LowLevel_GetFunctionNum();
  for (func = 0; func < n; func++)
  {
    //printf("-------------- CIS of Function %d ----------------\n", func);
    
    // 获取CIS的地址
    cis_ptr = (func << 8) | 0x9;
    cis_ptr  = WiFi_LowLevel_ReadReg(0, cis_ptr) | (WiFi_LowLevel_ReadReg(0, cis_ptr + 1) << 8) | (WiFi_LowLevel_ReadReg(0, cis_ptr + 2) << 16);
    //printf("Pointer to Function %d Card Information Structure (CIS): 0x%08x\n", func, cis_ptr);
    
    // 遍历CIS, 直到尾节点
    while ((tpl_code = WiFi_LowLevel_ReadReg(0, cis_ptr++)) != CISTPL_END)
    {
      if (tpl_code == CISTPL_NULL)
        continue;
      
      tpl_link = WiFi_LowLevel_ReadReg(0, cis_ptr++); // 本结点数据的大小
      for (i = 0; i < tpl_link; i++)
        data[i] = WiFi_LowLevel_ReadReg(0, cis_ptr + i);
      
      switch (tpl_code)
      {
        case CISTPL_VERS_1:
          printf("Product Information:");
          for (i = 2; data[i] != 0xff; i += len + 1)
          {
            // 遍历所有字符串
            len = strlen((char *)&data[i]);
            if (len != 0)
              printf(" %s", data + i);
          }
          printf("\n");
          break;
        case CISTPL_MANFID:
          // 16.6 CISTPL_MANFID: Manufacturer Identification String Tuple
          printf("Manufacturer Code: 0x%04x\n", *(uint16_t *)data); // TPLMID_MANF
          printf("Manufacturer Information: 0x%04x\n", *(uint16_t *)(data + 2)); // TPLMID_CARD
          break;
        case CISTPL_FUNCID:
          // 16.7.1 CISTPL_FUNCID: Function Identification Tuple
          printf("Card Function Code: 0x%02x\n", data[0]); // TPLFID_FUNCTION
          printf("System Initialization Bit Mask: 0x%02x\n", data[1]); // TPLFID_SYSINIT
          break;
        case CISTPL_FUNCE:
          // 16.7.2 CISTPL_FUNCE: Function Extension Tuple
          if (data[0] == 0)
          {
            // 16.7.3 CISTPL_FUNCE Tuple for Function 0 (Extended Data 00h)
            printf("Maximum Block Size: %d\n", *(uint16_t *)(data + 1));
            printf("Maximum Transfer Rate Code: 0x%02x\n", data[3]);
          }
          else
          {
            // 16.7.4 CISTPL_FUNCE Tuple for Function 1-7 (Extended Data 01h)
            printf("Maximum Block Size: %d\n", *(uint16_t *)(data + 0x0c)); // TPLFE_MAX_BLK_SIZE
          }
          break;
        default:
          printf("[CIS Tuple 0x%02x] addr=0x%08x size=%d\n", tpl_code, cis_ptr - 2, tpl_link);
          dump_data(data, tpl_link);
      }
      
      cis_ptr += tpl_link;
      if (tpl_link == 0xff)
        break; // 当TPL_LINK为0xff时说明当前结点为尾节点
    }
    //printf("--------------------------------------------------\n");
  }
}

/* 创建一个Ad-Hoc型的WiFi热点 */
// 创建带有WEP密码的热点时, cap_info为WIFI_CAPABILITY_PRIVACY
// 创建无密码的热点时, cap_info为0
void WiFi_StartADHOC(const char *ssid, uint16_t cap_info, WiFi_Callback callback, void *arg)
{
  WiFi_Cmd_ADHOCStart *cmd = (WiFi_Cmd_ADHOCStart *)wifi_tx_command.buffer;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  strncpy((char *)cmd->ssid, ssid, sizeof(cmd->ssid));
  cmd->bss_type = WIFI_BSS_INDEPENDENT;
  cmd->bcn_period = 100;
  cmd->ibss_param_set.header.type = WIFI_MRVLIETYPES_IBSSPARAMSET;
  cmd->ibss_param_set.header.length = TLV_PAYLOADLEN(cmd->ibss_param_set);
  cmd->ibss_param_set.atim_window = 0;
  cmd->ds_param_set.header.type = WIFI_MRVLIETYPES_DSPARAMSET;
  cmd->ds_param_set.header.length = TLV_PAYLOADLEN(cmd->ds_param_set);
  cmd->ds_param_set.channel = 1;
  cmd->cap_info = WIFI_CAPABILITY_IBSS | cap_info;
  *(uint32_t *)cmd->data_rate = 0x968b8482;
  WiFi_SendCommand(CMD_802_11_AD_HOC_START, wifi_tx_command.buffer, sizeof(WiFi_Cmd_ADHOCStart), callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 创建一个Ad-Hoc型的WiFi热点并设置好密码 */
// conn->mac_addr成员将被忽略
void WiFi_StartADHOCEx(const WiFi_Connection *conn, WiFi_Callback callback, void *arg)
{
  uint16_t ssid_len;
  void **p;
  if (WiFi_CheckCommandBusy(callback, arg))
    return;
  
  if (conn->security == WIFI_SECURITYTYPE_WPA || conn->security == WIFI_SECURITYTYPE_WPA2)
  {
    printf("WiFi_StartADHOCEx: WPA is not supported!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_INVALID);
    return;
  }
  
  ssid_len = strlen(conn->ssid);
  p = malloc(3 * sizeof(void *) + ssid_len + 1);
  if (p == NULL)
  {
    printf("WiFi_StartADHOCEx: malloc failed!\n");
    if (callback)
      callback(arg, NULL, WIFI_STATUS_MEM);
    return;
  }
  p[0] = arg;
  p[1] = callback;
  p[2] = (void *)conn->security;
  memcpy(p + 3, conn->ssid, ssid_len + 1);
  
  if (conn->security == WIFI_SECURITYTYPE_WEP)
    WiFi_SetWEP(WIFI_ACT_ADD, conn->password, WiFi_StartADHOCEx_Callback, p);
  else
    WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_StartADHOCEx_Callback, p);
}

static void WiFi_StartADHOCEx_Callback(void *arg, void *data, WiFi_Status status)
{
  void **p = (void **)arg;
  void *app_arg = p[0];
  WiFi_Callback app_callback = (WiFi_Callback)p[1];
  WiFi_SecurityType security = (WiFi_SecurityType)(uint32_t)p[2];
  char *ssid = (char *)(p + 3);
  
  if (status != WIFI_STATUS_OK)
  {
    printf("WiFi_StartADHOCEx error!\n");
    free(arg);
    if (app_callback)
      app_callback(app_arg, data, status);
    return;
  }
  
  switch (WiFi_GetCommandCode(data))
  {
    case CMD_802_11_SET_WEP:
      WiFi_MACControl(WIFI_MACCTRL_ETHERNET2 | WIFI_MACCTRL_WEP | WIFI_MACCTRL_TX | WIFI_MACCTRL_RX, WiFi_StartADHOCEx_Callback, arg);
      break;
    case CMD_MAC_CONTROL:
      if (security == WIFI_SECURITYTYPE_NONE)
        WiFi_StartADHOC(ssid, 0, WiFi_StartADHOCEx_Callback, arg);
      else
        WiFi_StartADHOC(ssid, WIFI_CAPABILITY_PRIVACY, WiFi_StartADHOCEx_Callback, arg);
      break;
    case CMD_802_11_AD_HOC_START:
      free(arg);
      if (app_callback)
        app_callback(app_arg, data, status);
      break;
  }
}

void WiFi_StopADHOC(WiFi_Callback callback, void *arg)
{
  WiFi_SendCommand(CMD_802_11_AD_HOC_STOP, wifi_tx_command.buffer, sizeof(WiFi_CommandHeader), callback, arg, WIFI_DEFAULT_TIMEOUT_CMDRESP, WIFI_DEFAULT_MAXRETRY);
}

/* 释放发送缓冲区并调用回调函数 */
static void WiFi_TxBufferComplete(WiFi_TxBuffer *tbuf, void *data, WiFi_Status status)
{
  if (tbuf->busy)
  {
    tbuf->busy = 0;
    if (tbuf->callback)
      tbuf->callback(tbuf->arg, data, status); // 调用回调函数
  }
}

/* 将IEEE型的TLV转换成MrvlIE型的TLV */
uint8_t WiFi_TranslateTLV(MrvlIEType *mrvlie_tlv, const IEEEType *ieee_tlv, uint16_t mrvlie_payload_size)
{
  mrvlie_tlv->header.type = ieee_tlv->header.type;
  if (ieee_tlv->header.length > mrvlie_payload_size)
    mrvlie_tlv->header.length = mrvlie_payload_size; // 若源数据大小超过缓冲区最大容量, 则丢弃剩余数据
  else
    mrvlie_tlv->header.length = ieee_tlv->header.length;
  memcpy(mrvlie_tlv->data, ieee_tlv->data, mrvlie_tlv->header.length); // 复制数据
  return mrvlie_tlv->header.length == ieee_tlv->header.length; // 返回值表示缓冲区大小是否足够
}

/* 在规定的超时时间内, 等待指定的卡状态位置位, 并清除相应的中断标志位 */
// 成功时返回1
uint8_t WiFi_Wait(uint8_t status, uint32_t timeout)
{
  if (timeout != 0)
    timeout += sys_now();
  
  while ((WiFi_LowLevel_ReadReg(1, WIFI_INTSTATUS) & status) != status)
  {
    if (timeout != 0 && sys_now() > timeout)
    {
      // 若超时时间已到
      printf("WiFi_Wait(0x%02x): timeout!\n", status);
      return 0;
    }
  }
  
  // 清除对应的中断标志位 (只针对download/upload ready)
  status = ~status & WIFI_INTSTATUS_ALL; // 不需要清除的位必须为1
  if ((WiFi_LowLevel_ReadReg(1, WIFI_INTSTATUS) & status) == 0) // 没有其他中断标志位时
    WiFi_LowLevel_ClearSDIOIT(); // 清除单片机上的中断标志位 (在此处清除可避免下面的操作错过新的中断)
  WiFi_LowLevel_WriteReg(1, WIFI_INTSTATUS, status);
  return 1;
}

/* 等待之前发送的命令帧或数据帧收到确认 */
void WiFi_WaitForLastTask(void)
{
  int32_t remaining;
#ifdef WIFI_DISPLAY_RESPTIME
  WiFi_CommandHeader *tx_cmd = (WiFi_CommandHeader *)wifi_tx_command.buffer;
#endif
  
  // 发送新数据帧前, 必须等待之前发送的命令或数据收到DNLDRDY的确认
  // 注: CMDBUSY=DATABUSY=1, CMDRDY=0这种情况是不可能出现的
  while ((wifi_tx_command.busy && wifi_tx_command.ready == 0) || wifi_tx_packet.busy)
  {
    if (wifi_tx_command.busy && wifi_tx_command.ready == 0)
    {
      remaining = wifi_tx_command.start_time + WIFI_DEFAULT_TIMEOUT_CMDACK - sys_now() + 1;
      if (remaining > 0)
      {
        if (WiFi_Wait(WIFI_INTSTATUS_DNLD, remaining))
        {
#ifdef WIFI_DISPLAY_RESPTIME
          printf("-- CMD 0x%04x ACK at %dms\n", tx_cmd->cmd_code, sys_now() - wifi_tx_command.start_time);
#endif
        }
      }
      wifi_tx_command.ready = 1;
      
      // 现在命令要么超时, 要么已收到确认
      // 命令内容不占用数据发送缓冲区, 因此这里不负责命令的重传
    }
    
    if (wifi_tx_packet.busy)
    {
      // 这种问题通常会出现在使用TCP发送大量数据的时候
      // 只需要等待download ready位置位, 不用管新来的数据帧(upload ready)
      remaining = wifi_tx_packet.start_time + wifi_tx_packet.timeout - sys_now() + 1; // 剩余时间+1 (小于等于0表示超时)
      if (remaining > 0 && WiFi_Wait(WIFI_INTSTATUS_DNLD, remaining)) // 在剩余时间+1内等待标志位置位, 并清除中断标志位
      {
#ifdef WIFI_DISPLAY_RESPTIME
        printf("-- Packet ACK at %dms\n", sys_now() - wifi_tx_packet.start_time);
#endif
        WiFi_TxBufferComplete(&wifi_tx_packet, wifi_tx_packet.buffer, WIFI_STATUS_OK); // 若DNLDRDY已置位, 则表明数据帧发送成功, 将busy清零并调用相应的回调函数
        // 如果在回调函数中发送了新帧, 那么busy仍等于1, 需要继续等待, 所以这里不能写break
      }
      else if (WiFi_CheckTxBufferRetry(&wifi_tx_packet, wifi_tx_packet.buffer)) // 超时未置位: 判断是否需要重传, 且重传次数未达到最大值
      {
        WiFi_SendPacket(NULL, 0, wifi_tx_packet.callback, wifi_tx_packet.arg, wifi_tx_packet.timeout, wifi_tx_packet.retry - 1);
        printf("-- WiFi Packet Timeout! Resend...\n");
      }
      /*
      else: 不需要重传的情况: 超时时间到了但达到了最大重传次数(busy=0)
      有可能在回调函数中又发送了新的数据帧, 所以需要在下一次循环中判断busy是否为1, 继续等待
      */
    }
  }
}
WiFi.h:

/* 选项 */
#define WIFI_DEFAULT_BUFSIZE 256 // WiFi命令默认的缓冲区大小(兼容不支持字节流传输的SDIO高速模式)
#define WIFI_DEFAULT_MAXRETRY 5 // 数据默认最大尝试发送的次数
#define WIFI_DEFAULT_TIMEOUT_CMDACK 20 // WiFi命令帧确认的超时时间(ms)
#define WIFI_DEFAULT_TIMEOUT_CMDRESP 300 // WiFi命令帧回应的超时时间(ms)
#define WIFI_DEFAULT_TIMEOUT_DATAACK 20 // WiFi数据帧确认的超时时间(ms)
#define WIFI_DISPLAY_PACKET_SIZE // 显示收发的数据包的大小
//#define WIFI_DISPLAY_PACKET_RX // 显示收到的数据包内容
//#define WIFI_DISPLAY_PACKET_TX // 显示发送的数据包内容
//#define WIFI_DISPLAY_RESPTIME // 显示命令帧和数据帧从发送到收到确认和回应所经过的时间
#define WIFI_HIGHSPEED // 采用SDIO高速模式
#define WIFI_USEDMA // SDIO采用DMA方式收发数据

// 高速模式下必须使用DMA
#if defined(WIFI_HIGHSPEED) && !defined(WIFI_USEDMA)
#define WIFI_USEDMA
#endif

#define WIFI_FIRMWAREAREA_ADDR 0x08061000 // 固件存储区首地址

#ifdef WIFI_FIRMWAREAREA_ADDR
// 固件存储区的格式: helper固件大小(4B)+固件大小(4B)+helper固件内容+固件内容+CRC校验码(4B)
#define WIFI_HELPER_SIZE (*(uint32_t *)WIFI_FIRMWAREAREA_ADDR)
#define WIFI_FIRMWARE_SIZE (*(uint32_t *)(WIFI_FIRMWAREAREA_ADDR + 4))
#define WIFI_HELPER_ADDR ((const uint8_t *)WIFI_FIRMWAREAREA_ADDR + 8)
#define WIFI_FIRMWARE_ADDR (WIFI_HELPER_ADDR + WIFI_HELPER_SIZE)
#else
extern const unsigned char firmware_helper_sd[2516];
extern const unsigned char firmware_sd8686[122916];
#define WIFI_HELPER_SIZE sizeof(firmware_helper_sd)
#define WIFI_FIRMWARE_SIZE sizeof(firmware_sd8686)
#define WIFI_HELPER_ADDR firmware_helper_sd
#define WIFI_FIRMWARE_ADDR firmware_sd8686
#endif

/* WiFi寄存器及位定义 */
#define _BV(n) (1u << (n))

// 6.9 Card Common Control Registers (CCCR)
#define SDIO_CCCR_IOEN 0x02
#define SDIO_CCCR_IOEN_IOE1 _BV(1)

#define SDIO_CCCR_IORDY 0x03
#define SDIO_CCCR_IORDY_IOR1 _BV(1)

#define SDIO_CCCR_INTEN 0x04
#define SDIO_CCCR_INTEN_IENM _BV(0)
#define SDIO_CCCR_INTEN_IEN1 _BV(1)

#define SDIO_CCCR_BUSIFCTRL 0x07 // Bus Interface Control
#define SDIO_CCCR_BUSIFCTRL_BUSWID_1Bit 0
#define SDIO_CCCR_BUSIFCTRL_BUSWID_4Bit 0x02
#define SDIO_CCCR_BUSIFCTRL_BUSWID_8Bit 0x03

#define WIFI_IOPORT0 0x00
#define WIFI_IOPORT1 0x01
#define WIFI_IOPORT2 0x02

#define WIFI_INTMASK 0x04 // Host Interrupt Mask
#define WIFI_INTMASK_HOSTINTMASK 0x0f // enable/disable SDU to SD host interrupt

#define WIFI_INTSTATUS 0x05 // Host Interrupt Status
#define WIFI_INTSTATUS_ALL 0x0f
#define WIFI_INTSTATUS_OVERFLOW _BV(3)
#define WIFI_INTSTATUS_UNDERFLOW _BV(2)
#define WIFI_INTSTATUS_DNLD _BV(1) // Download Host Interrupt Status
#define WIFI_INTSTATUS_UPLD _BV(0) // Upload Host Interrupt Status (可随时手动清除, 无论UPLDCARDRDY是否为1)

#define WIFI_SQREADBASEADDR0 0x10
#define WIFI_SQREADBASEADDR1 0x11
#define WIFI_SQREADBASEADDR2 0x12
#define WIFI_SQREADBASEADDR3 0x13

#define WIFI_CARDSTATUS 0x20 // Card Status
//#define WIFI_CARDSTATUS_IOREADY _BV(3) // I/O Ready Indicator
//#define WIFI_CARDSTATUS_CISCARDRDY _BV(2) // Card Information Structure Card Ready
//#define WIFI_CARDSTATUS_UPLDCARDRDY _BV(1) // Upload Card Ready (CMD53读写命令均会清除该位)
//#define WIFI_CARDSTATUS_DNLDCARDRDY _BV(0) // Download Card Ready

#define WIFI_SCRATCHPAD4_0 0x34
#define WIFI_SCRATCHPAD4_1 0x35

/* WiFi模块用到的枚举类型 */
// Authentication Type to be used to authenticate with AP
typedef enum
{
  WIFI_AUTH_MODE_OPEN = 0x00,
  WIFI_AUTH_MODE_SHARED = 0x01,
  WIFI_AUTH_MODE_NETWORK_EAP = 0x80
} WiFi_AuthenticationType;

// BSS type
typedef enum
{
  WIFI_BSS_INFRASTRUCTURE = 0x01,
  WIFI_BSS_INDEPENDENT = 0x02,
  WIFI_BSS_ANY = 0x03
} WiFi_BSSType;

// 部分WiFi命令的action字段
typedef enum
{
  WIFI_ACT_GET = 0,
  WIFI_ACT_SET = 1,
  WIFI_ACT_ADD = 2,
  WIFI_ACT_BITWISE_SET = 2,
  WIFI_ACT_BITWISE_CLR = 3,
  WIFI_ACT_REMOVE = 4
} WiFi_CommandAction;

// WiFi命令列表
typedef enum
{
  CMD_802_11_SCAN = 0x0006, // Starts the scan process
  CMD_802_11_ASSOCIATE = 0x0012, // Initiate an association with the AP
  CMD_802_11_SET_WEP = 0x0013, // Configures the WEP keys
  CMD_802_11_DEAUTHENTICATE = 0x0024, // Starts de-authentication process with the AP
  CMD_MAC_CONTROL = 0x0028, // Controls hardware MAC
  CMD_802_11_AD_HOC_START = 0x002b, // Starts an Ad-Hoc network
  CMD_802_11_AD_HOC_JOIN = 0x002c, // Join an Ad-Hoc network
  CMD_802_11_AD_HOC_STOP = 0x0040, // Stops Ad-Hoc Network
  CMD_802_11_MAC_ADDR = 0x004d, // WLAN MAC address
  CMD_802_11_KEY_MATERIAL = 0x005e, // Gets/sets key material used to do Tx encryption or Rx decryption
  CMD_802_11_BG_SCAN_CONFIG = 0x006b, // Gets/sets background scan configuration
  CMD_802_11_BG_SCAN_QUERY = 0x006c, // Gets background scan results
  CMD_802_11_SUBSCRIBE_EVENT = 0x0075 // Subscribe to events and set thresholds
} WiFi_CommandList;

// WiFi命令执行结果
typedef enum
{
  CMD_STATUS_SUCCESS = 0x0000, // No error
  CMD_STATUS_ERROR = 0x0001, // Command failed
  CMD_STATUS_UNSUPPORTED = 0x0002 // Command is not supported (result=2表示WiFi模块不支持此命令)
} WiFi_CommandResult;

// WiFi密钥类型
typedef enum
{
  WIFI_KEYTYPE_WEP = 0,
  WIFI_KEYTYPE_TKIP = 1,
  WIFI_KEYTYPE_AES = 2
} WiFi_KeyType;

// Table 45: IEEE 802.11 Standard IE Translated to Marvell IE
// PDF中的表45有一些拼写错误, MRVIIE应该改为MRVLIE
typedef enum
{
  WIFI_MRVLIETYPES_SSIDPARAMSET = 0x0000,
  WIFI_MRVLIETYPES_RATESPARAMSET = 0x0001,
  WIFI_MRVLIETYPES_DSPARAMSET = 0x0003,
  WIFI_MRVLIETYPES_CFPARAMSET = 0x0004,
  WIFI_MRVLIETYPES_IBSSPARAMSET = 0x0006,
  WIFI_MRVLIETYPES_RSNPARAMSET = 0x0030,
  WIFI_MRVLIETYPES_VENDORPARAMSET = 0x00dd,
  WIFI_MRVLIETYPES_KEYPARAMSET = 0x0100,
  WIFI_MRVLIETYPES_CHANLISTPARAMSET = 0x0101,
  WIFI_MRVLIETYPES_TSFTIMESTAMP = 0x0113,
  WIFI_MRVLIETYPES_AUTHTYPE = 0x011f
} WiFi_MrvlIETypes;

// SDIO帧类型
typedef enum
{
  WIFI_SDIOFRAME_DATA = 0x00,
  WIFI_SDIOFRAME_COMMAND = 0x01,
  WIFI_SDIOFRAME_EVENT = 0x03
} WiFi_SDIOFrameType;

// 16.5 SDIO Card Metaformat
typedef enum
{
  CISTPL_NULL = 0x00, // Null tuple
  CISTPL_VERS_1 = 0x15, // Level 1 version/product-information
  CISTPL_MANFID = 0x20, // Manufacturer Identification String Tuple
  CISTPL_FUNCID = 0x21, // Function Identification Tuple
  CISTPL_FUNCE = 0x22, // Function Extensions
  CISTPL_END = 0xff // The End-of-chain Tuple
} WiFi_SDIOTupleCode;

// WEP密钥长度
typedef enum
{
  WIFI_WEPKEYTYPE_40BIT = 1,
  WIFI_WEPKEYTYPE_104BIT = 2
} WiFi_WEPKeyType;

// 无线认证类型
typedef enum
{
  WIFI_SECURITYTYPE_NONE = 0,
  WIFI_SECURITYTYPE_WEP = 1,
  WIFI_SECURITYTYPE_WPA = 2,
  WIFI_SECURITYTYPE_WPA2 = 3
} WiFi_SecurityType;

// 回调函数中的状态参数
typedef enum
{
  WIFI_STATUS_OK = 0, // 成功收到了回应
  WIFI_STATUS_FAIL = 1, // 未能完成请求的操作 (例如找到了AP热点但关联失败)
  WIFI_STATUS_BUSY = 2, // 之前的操作尚未完成
  WIFI_STATUS_NORESP = 3, // 重试了几遍都没有收到回应
  WIFI_STATUS_MEM = 4, // 内存不足
  WIFI_STATUS_INVALID = 5, // 无效的参数
  WIFI_STATUS_NOTFOUND = 6, // 未找到目标 (如AP热点)
  WIFI_STATUS_INPROGRESS = 7 // 成功执行命令, 但还需要后续的操作 (比如关联AP成功但还需要后续的认证操作)
} WiFi_Status;

/* 回调函数类型 */
typedef void (*WiFi_Callback)(void *arg, void *data, WiFi_Status status); // data为NULL表示没有收到任何回应

/* WiFi命令字段位定义 */
// Capability information
#define WIFI_CAPABILITY_BSS _BV(0)
#define WIFI_CAPABILITY_IBSS _BV(1)
#define WIFI_CAPABILITY_CF_POLLABLE _BV(2)
#define WIFI_CAPABILITY_CF_POLL_REQUEST _BV(3)
#define WIFI_CAPABILITY_PRIVACY _BV(4)
#define WIFI_CAPABILITY_SHORT_PREAMBLE _BV(5)
#define WIFI_CAPABILITY_PBCC _BV(6)
#define WIFI_CAPABILITY_CHANNEL_AGILITY _BV(7)
#define WIFI_CAPABILITY_SPECTRUM_MGMT _BV(8)
#define WIFI_CAPABILITY_QOS _BV(9)
#define WIFI_CAPABILITY_SHORT_SLOT _BV(10)
#define WIFI_CAPABILITY_DSSS_OFDM _BV(13)

#define WIFI_KEYINFO_KEYENABLED _BV(2)
#define WIFI_KEYINFO_UNICASTKEY _BV(1)
#define WIFI_KEYINFO_MULTICASTKEY _BV(0)

#define WIFI_MACCTRL_RX _BV(0)
#define WIFI_MACCTRL_TX _BV(1) // 此位必须要置1才能发送数据!!!
#define WIFI_MACCTRL_LOOPBACK _BV(2)
#define WIFI_MACCTRL_WEP _BV(3)
#define WIFI_MACCTRL_ETHERNET2 _BV(4)
#define WIFI_MACCTRL_PROMISCUOUS _BV(7)
#define WIFI_MACCTRL_ALLMULTICAST _BV(8)
#define WIFI_MACCTRL_ENFORCEPROTECTION _BV(10) // strict protection
#define WIFI_MACCTRL_ADHOCGPROTECTIONMODE _BV(13) // 802.11g protection mode

/* 常用的宏函数 */
// 已知结构体大小sizeof(tlv), 求数据域的大小, 一般用于给header.length赋值 */
// 例如定义一个MrvlIETypes_CfParamSet_t param变量, 赋值param.header.length=TLV_PAYLOADLEN(param)
#define TLV_PAYLOADLEN(tlv) (sizeof(tlv) - sizeof((tlv).header))

// 已知数据域大小, 求整个结构体的大小 */
// 例如定义一个很大的buffer, 然后定义一个IEEEType *的指针p指向该buffer
// buffer接收到数据后, 要求出接收到的IEEEType数据的实际大小显然不能用sizeof(IEEEType), 因为定义IEEEType结构体时data的长度定义的是1
// 此时就应该使用TLV_STRUCTLEN(*p)
#define TLV_STRUCTLEN(tlv) (sizeof((tlv).header) + (tlv).header.length)

// 已知本TLV的地址和大小, 求下一个TLV的地址
#define TLV_NEXT(tlv) ((uint8_t *)(tlv) + TLV_STRUCTLEN(*(tlv)))

// 字节序转换函数
#ifndef htons
#define htons(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
#endif
#ifndef ntohs
#define ntohs htons
#endif

#define WiFi_GetCommandCode(data) (((data) == NULL) ? 0 : (((const WiFi_CommandHeader *)(data))->cmd_code & 0x7fff))
#define WiFi_IsCommandResponse(data) ((data) != NULL && ((const WiFi_CommandHeader *)(data))->cmd_code & 0x8000)

/* TLV (Tag Length Value) of IEEE IE Type Format */
typedef __packed struct
{
	uint8_t type;
	uint8_t length; // 数据域的大小
} IEEEHeader;

// information element parameter
// 所有IEEETypes_*类型的基类型
typedef __packed struct
{
	IEEEHeader header;
	uint8_t data[1];
} IEEEType;

typedef __packed struct
{
	IEEEHeader header;
	uint8_t channel;
} IEEETypes_DsParamSet_t;

typedef __packed struct
{
	IEEEHeader header;
	uint16_t atim_window;
} IEEETypes_IbssParamSet_t;

/* TLV (Tag Length Value) of MrvllEType Format */
typedef __packed struct
{
	uint16_t type;
	uint16_t length;
} MrvlIEHeader;

// 所有MrvlIETypes_*类型的基类型
typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t data[1];
} MrvlIEType;

typedef __packed struct
{
	MrvlIEHeader header;
	uint16_t auth_type;
} MrvlIETypes_AuthType_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t count;
	uint8_t period;
	uint16_t max_duration;
	uint16_t duration_remaining;
} MrvlIETypes_CfParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	__packed struct
	{
		uint8_t band_config_type;
		uint8_t chan_number;
		uint8_t scan_type;
		uint16_t min_scan_time;
		uint16_t max_scan_time;
	} channels[1];
} MrvlIETypes_ChanListParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t channel;
} MrvlIETypes_PhyParamDSSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint16_t key_type_id;
	uint16_t key_info;
	uint16_t key_len;
	uint8_t key[32];
} MrvlIETypes_KeyParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t rates[14];
} MrvlIETypes_RatesParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t rsn[64];
} MrvlIETypes_RsnParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t ssid[32];
} MrvlIETypes_SSIDParamSet_t;

typedef __packed struct
{
	MrvlIEHeader header;
	uint64_t tsf_table[1];
} MrvlIETypes_TsfTimestamp_t;

// 整个结构体的最大大小为256字节
typedef __packed struct
{
	MrvlIEHeader header;
	uint8_t vendor[64]; // 通常情况下64字节已足够
} MrvlIETypes_VendorParamSet_t;

/* WiFi命令帧和数据帧格式 */
// WiFi模块所有类型的帧的头部
typedef __packed struct
{
	uint16_t length; // 大小包括此成员本身
	uint16_t type;
} WiFi_SDIOFrameHeader;

// WiFi模块命令帧的头部
typedef __packed struct
{
	WiFi_SDIOFrameHeader frame_header;
	uint16_t cmd_code;
	uint16_t size;
	uint16_t seq_num;
	uint16_t result;
} WiFi_CommandHeader;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint8_t ssid[32];
	uint8_t bss_type;
	uint16_t bcn_period;
	uint8_t reserved1;
	IEEETypes_IbssParamSet_t ibss_param_set; // ATIM window length in TU
	uint32_t reserved2;
	IEEETypes_DsParamSet_t ds_param_set; // The channel for ad-hoc network
	uint16_t reserved3[3];
	uint16_t cap_info; // Capability information
	uint8_t data_rate[14];
} WiFi_Cmd_ADHOCStart;

typedef __packed struct
{
  WiFi_CommandHeader header;
  uint8_t bssid[6]; // MAC address
  uint8_t ssid[32];
  uint8_t bss_type;
  uint16_t bcn_period;
  uint8_t dtim_period; // Specify DTIM period (TBTTs)
  uint8_t timestamp[8];
  uint8_t start_ts[8]; // Starting timestamp
  IEEETypes_DsParamSet_t ds_param_set; // IEEE DS parameter set element
  uint32_t reserved1;
  IEEETypes_IbssParamSet_t ibss_param_set; // IEEE IBSS parameter set
  uint32_t reserved2;
  uint16_t cap_info;
  uint8_t data_rates[14];
  uint32_t reserved3;
} WiFi_Cmd_ADHOCJoin;

typedef __packed struct
{
  WiFi_CommandHeader header;
  uint8_t peer_sta_addr[6];
  uint16_t reason_code; // Reason code defined in IEEE 802.11 specification section 7.3.1.7 to indicate de-authentication reason
} WiFi_Cmd_Deauthenticate;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint16_t action;
} WiFi_Cmd_KeyMaterial;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint16_t action;
	uint8_t mac_addr[6];
} WiFi_Cmd_MACAddr;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint16_t action;
	uint16_t reserved;
} WiFi_Cmd_MACCtrl;

typedef __packed struct
{
  WiFi_CommandHeader header;
  uint16_t action;
  uint16_t tx_key_index; // Key set being used for transmit (0~3)
  uint8_t wep_types[4]; // use 40 or 104 bits
  uint8_t keys[4][16];
} WiFi_Cmd_SetWEP;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint8_t peer_sta_addr[6]; // Peer MAC address
	uint16_t cap_info; // Capability information
	uint16_t listen_interval; // Listen interval
	uint16_t bcn_period; // Beacon period
	uint8_t dtim_period; // DTIM period
} WiFi_CmdRequest_Associate;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint8_t bss_type;
	uint8_t bss_id[6];
} WiFi_CmdRequest_Scan;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint16_t capability;
	uint16_t status_code;
	uint16_t association_id;
	IEEEType ie_buffer;
} WiFi_CmdResponse_Associate;

typedef __packed struct
{
	WiFi_CommandHeader header;
	uint16_t buf_size;
	uint8_t num_of_set;
} WiFi_CmdResponse_Scan;

// WiFi模块接收的数据帧
// Table 2: Fields in Receive Packet Descriptor
typedef __packed struct
{
	WiFi_SDIOFrameHeader header;
	uint16_t reserved1;
	uint8_t snr; // Signal to noise ratio for this packet (dB)
	uint8_t reserved2;
	uint16_t rx_packet_length; // Number of bytes in the payload
	uint8_t nf; // Noise floor for this packet (dBm). Noise floor is always negative. The absolute value is passed.
	uint8_t rx_rate; // Rate at which this packet is received
	uint32_t rx_packet_offset; // Offset from the start of the packet to the beginning of the payload data packet
	uint32_t reserved3;
	uint8_t priority; // Specifies the user priority of received packet
	uint8_t reserved4[3];
	uint8_t payload[1]; // 数据链路层上的帧
} WiFi_DataRx;

// WiFi模块发送的数据帧
// Table 3: Fields in Transmit Packet Descriptor
typedef __packed struct
{
	WiFi_SDIOFrameHeader header;
	uint32_t reserved1;
	uint32_t tx_control; // See 3.2.1 Per-Packet Settings
	uint32_t tx_packet_offset; // Offset of the beginning of the payload data packet (802.3 or 802.11 frames) from the beginning of the packet (bytes)
	uint16_t tx_packet_length; // Number of bytes in the payload data frame
	uint16_t tx_dest_addr_high; // Destination MAC address bytes 4 to 5
	uint32_t tx_dest_addr_low; // Destination MAC address bytes 0 to 3
	uint8_t priority; // Specifies the user priority of transmit packet
	uint8_t flags;
	uint8_t pkt_delay_2ms; // Amount of time the packet has been queued in the driver layer for WMM implementations
	uint8_t reserved2;
	uint8_t payload[1]; // 数据链路层上的帧
} WiFi_DataTx;

// EAPOL认证帧
// https://www.vocal.com/secure-communication/eapol-extensible-authentication-protocol-over-lan/
typedef __packed struct
{
  uint8_t dest[6];
  uint8_t src[6];
  uint16_t type;
  uint8_t version;
  uint8_t packet_type;
  uint16_t packet_body_length; // big endian
  
  /* packet body */
  // 802.11-2016.pdf: Figure 12-32 EAPOL-Key frame
  // http://etutorials.org/Networking/802.11+security.+wi-fi+protected+access+and+802.11i/Part+II+The+Design+of+Wi-Fi+Security/Chapter+10.+WPA+and+RSN+Key+Hierarchy/Details+of+Key+Derivation+for+WPA/
  uint8_t descriptor_type;
  uint16_t key_information;
  uint16_t key_length;
  uint8_t key_replay_counter[8];
  uint8_t key_nonce[32];
  uint8_t key_iv[16];
  uint8_t key_rsc[8]; // receive sequence counter
  uint8_t reserved[8]; // not used in WPA
  uint8_t key_mic[16];
  uint16_t key_data_length;
  uint8_t key_data[1];
} WiFi_EAPOLKeyFrame;

// WiFi模块事件帧
typedef __packed struct
{
  WiFi_SDIOFrameHeader header;
  uint32_t event_id; // Enumerated identifier for the event
  uint16_t reason_code; // IEEE Reason Code as described in the 802.11 standard
  uint8_t mac_addr[6]; // Peer STA Address
} WiFi_Event;

/* WiFi的常用数据结构 */
typedef __packed struct
{
	uint16_t ie_length; // Total information element length (不含sizeof(ie_length))
	uint8_t bssid[6]; // BSSID
	uint8_t rssi; // RSSI value as received from peer
	
	// Probe Response/Beacon Payload
	uint64_t pkt_time_stamp; // Timestamp
	uint16_t bcn_interval; // Beacon interval
	uint16_t cap_info; // Capabilities information
	IEEEType ie_parameters; // 存放的是一些IEEE类型的数据
} WiFi_BssDescSet;

typedef struct
{
  WiFi_SecurityType security;
  char *ssid;
  void *password;
  uint8_t *mac_addr; // 用于接收MAC地址 (不能是局部变量)
} WiFi_Connection;

typedef __packed struct
{
  uint8_t TK[16];
  uint8_t TKIPTxMICKey[8];
  uint8_t TKIPRxMICKey[8];
} WiFi_GTK;

typedef __packed struct
{
  uint8_t type;
  uint8_t length;
  uint8_t oui[3];
  uint8_t data_type;
  uint8_t data[1];
} WiFi_KDE;

typedef __packed struct
{
  uint8_t MIC[16];
  uint8_t RESERVED[4]; // 用于给sha1函数留足缓冲区
} WiFi_MIC;

typedef __packed struct
{
  uint8_t KCK[16];
  uint8_t KEK[16];
  uint8_t TK[16];
  uint8_t TKIPTxMICKey[8];
  uint8_t TKIPRxMICKey[8];
} WiFi_PTK;

typedef __packed struct
{
  uint8_t MAC[2][6];
  uint8_t nonce[2][32];
} WiFi_PTKB;

typedef struct
{
  uint8_t buffer[2048];
  WiFi_Callback callback;
  void *arg;
  uint8_t busy;
  uint8_t ready;
  uint8_t retry;
  uint32_t start_time;
  uint32_t timeout;
} WiFi_TxBuffer;

typedef __packed struct
{
	uint8_t oui[3];
	uint8_t oui_type;
	uint8_t oui_subtype;
	uint8_t version;
} WiFi_Vendor;

// WiFi热点信息
typedef struct
{
	MrvlIETypes_SSIDParamSet_t ssid;
	uint8_t mac_addr[6];
	uint16_t cap_info;
	uint16_t bcn_period;
	uint8_t channel;
	MrvlIETypes_RatesParamSet_t rates;
	MrvlIETypes_RsnParamSet_t rsn;
	MrvlIETypes_VendorParamSet_t wpa;
	MrvlIETypes_VendorParamSet_t wwm;
	MrvlIETypes_VendorParamSet_t wps;
} WiFi_SSIDInfo;

typedef struct
{
  char *keys[4];
  uint8_t index; // 0~3
} WiFi_WEPKey;

/* WiFi模块底层函数 */
void WiFi_LowLevel_ClearSDIOIT(void);
uint8_t WiFi_LowLevel_GetFunctionNum(void);
void WiFi_LowLevel_Init(void);
uint8_t WiFi_LowLevel_ReadData(uint8_t func, uint32_t addr, void *data, uint32_t size, uint32_t bufsize);
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr);
void WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size);
uint8_t WiFi_LowLevel_WriteData(uint8_t func, uint32_t addr, const void *data, uint32_t size, uint32_t bufsize);
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value);

/* WiFi模块主要函数 */
void WiFi_Associate(const char *ssid, WiFi_AuthenticationType auth_type, uint8_t *mac_addr, WiFi_Callback callback, void *arg);
void WiFi_AssociateEx(const WiFi_Connection *conn, WiFi_AuthenticationType auth_type, int32_t max_retry, WiFi_Callback callback, void *arg);
void WiFi_CheckTimeout(void);
void WiFi_Deauthenticate(const uint8_t mac_addr[6], uint16_t reason, WiFi_Callback callback, void *arg);
void WiFi_DiscardData(void);
void WiFi_GetMACAddress(WiFi_Callback callback, void *arg);
uint8_t *WiFi_GetPacketBuffer(void);
uint16_t WiFi_GetPacketLength(void);
const uint8_t *WiFi_GetReceivedPacket(uint16_t *len);
WiFi_SecurityType WiFi_GetSecurityType(const WiFi_SSIDInfo *info);
void WiFi_Init(void);
void WiFi_Input(void);
uint8_t WiFi_IsCommandBusy(void);
void WiFi_JoinADHOC(const char *ssid, uint8_t *mac_addr, WiFi_Callback callback, void *arg);
void WiFi_JoinADHOCEx(const WiFi_Connection *conn, int32_t max_retry, WiFi_Callback callback, void *arg);
void WiFi_KeyMaterial(WiFi_CommandAction action, MrvlIETypes_KeyParamSet_t *key, uint8_t key_count, WiFi_Callback callback, void *arg);
void WiFi_MACAddr(const uint8_t newaddr[6], WiFi_CommandAction action, WiFi_Callback callback, void *arg);
void WiFi_MACControl(uint16_t action, WiFi_Callback callback, void *arg);
void WiFi_Scan(WiFi_Callback callback, void *arg);
void WiFi_ScanSSID(const char *ssid, WiFi_SSIDInfo *info, WiFi_Callback callback, void *arg);
void WiFi_SendCommand(uint16_t code, const void *data, uint16_t size, WiFi_Callback callback, void *arg, uint32_t timeout, uint8_t max_retry);
void WiFi_SendPacket(void *data, uint16_t size, WiFi_Callback callback, void *arg, uint32_t timeout, uint8_t max_retry);
void WiFi_SetWEP(WiFi_CommandAction action, const WiFi_WEPKey *key, WiFi_Callback callback, void *arg);
void WiFi_SetWPA(const char *ssid, const char *password);
void WiFi_ShowCIS(void);
void WiFi_StartADHOC(const char *ssid, uint16_t cap_info, WiFi_Callback callback, void *arg);
void WiFi_StartADHOCEx(const WiFi_Connection *conn, WiFi_Callback callback, void *arg);
void WiFi_StopADHOC(WiFi_Callback callback, void *arg);
uint8_t WiFi_TranslateTLV(MrvlIEType *mrvlie_tlv, const IEEEType *ieee_tlv, uint16_t mrvlie_payload_size);
uint8_t WiFi_Wait(uint8_t status, uint32_t timeout);
void WiFi_WaitForLastTask(void);

/* 外部自定义回调函数 */
void WiFi_AuthenticationCompleteHandler(void);
void WiFi_EventHandler(const WiFi_Event *event);
void WiFi_PacketHandler(const WiFi_DataRx *data);


WPA.c:

#include <aes.h>
#include <netif/ppp/polarssl/arc4.h>
#include <netif/ppp/polarssl/md5.h>
#include <netif/ppp/polarssl/sha1.h>
#include <stdlib.h>
#include <string.h>
#include "WPA.h"

/* ARC4算法解密TKIP Key Data封包 */
// ARC4的加解密算法相同, 但每次都必须要调用一次arc4_setup函数
// 参考文章: http://www.fenlog.com/post/111.html
void ARC4_decrypt_keydata(const uint8_t *KEK, const uint8_t *key_iv, const uint8_t *data, uint16_t datalen, uint8_t *output)
{
  arc4_context ctx;
  uint8_t dummy[256] = {0};
  uint8_t newkey[2][16];
  
  memcpy(newkey[0], key_iv, sizeof(newkey[0]));
  memcpy(newkey[1], KEK, sizeof(newkey[1]));
  
  arc4_setup(&ctx, newkey[0], sizeof(newkey));
  arc4_crypt(&ctx, dummy, sizeof(dummy)); // discard the first 256 bytes of the RC4 cipher stream output
  memcpy(output, data, datalen);
  arc4_crypt(&ctx, output, datalen);
}

/* AES Key Wrap解密算法解密AES Key Data封包 */
// 88W8686不支持CMD_802_11_CRYPTO命令, 因此必须用软件实现此算法
// RFC3394.pdf: 2.2.2 Key Unwrap
// 返回值为输出结果的数据大小
uint16_t AES_unwrap(const uint8_t *key, const uint8_t *data, uint16_t datalen, uint8_t *output)
{
  uint8_t a[8], b[16];
  uint8_t *r;
  uint16_t i, j, n, t;
  struct AES_ctx ctx;
  AES_init_ctx(&ctx, key);
  
  /* Initialize variables */
  n = (datalen / 8) - 1;
  memcpy(a, data, 8);
  r = output;
  memcpy(r, data + 8, datalen - 8);

  /* Compute intermediate values */
  for (j = 5; j <= 5; j--)
  {
    r = output + (n - 1) * 8;
    for (i = n; i >= 1; i--)
    {
      t = n * j + i;
      memcpy(b, a, 8);
      b[7] ^= t;
      memcpy(b + 8, r, 8);

      AES_ECB_decrypt(&ctx, b);
      memcpy(a, b, 8);
      memcpy(r, b + 8, 8);
      r -= 8;
    }
  }
  return datalen - 8;
}

/* HMAC算法 */
// https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
// hash函数不会改变input参数的值, 所以可以放心去掉const修饰符
uint8_t hmac(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, HashFunction hash, uint8_t blocksize, uint8_t *output, uint8_t outputsize)
{
  uint8_t i, *p;
  uint8_t *obuf = (uint8_t *)malloc(blocksize + outputsize); // 用最前面的blocksize字节存放转换后的key
  if (obuf == NULL)
    return 0; // 计算失败: 内存不足

  // Keys longer than blockSize are shortened by hashing them
  if (keylen > blocksize)
  {
    hash((uint8_t *)key, keylen, obuf); // Key becomes outputSize bytes long
    keylen = outputsize;
  }
  else
    memcpy(obuf, key, keylen);

  // Keys shorter than blockSize are padded to blockSize by padding with zeros on the right
  if (keylen < blocksize)
    memset(obuf + keylen, 0, blocksize - keylen); // pad key with zeros to make it blockSize bytes long

  // Inner padded key
  p = (uint8_t *)malloc(blocksize + msglen);
  if (p == NULL)
  {
    free(obuf);
    return 0; // 计算失败: 内存不足
  }
  for (i = 0; i < blocksize; i++)
    p[i] = obuf[i] ^ 0x36;

  memcpy(p + blocksize, msg, msglen);
  hash(p, blocksize + msglen, obuf + blocksize);
  free(p);

  // Outer padded key
  for (i = 0; i < blocksize; i++)
    obuf[i] ^= 0x5c;
  
  hash(obuf, blocksize + outputsize, (uint8_t *)output);
  free(obuf);
  return 1; // 计算成功
}

/* 利用lwip提供的md5函数实现HMAC_MD5算法 */
uint8_t hmac_md5(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, uint8_t output[HMAC_MD5_OUTPUTSIZE])
{
  return hmac(key, keylen, msg, msglen, md5, HMAC_MD5_BLOCKSIZE, output, HMAC_MD5_OUTPUTSIZE);
}

/* 利用lwip提供的sha1函数实现HMAC_SHA1算法 */
uint8_t hmac_sha1(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, uint8_t output[HMAC_SHA1_OUTPUTSIZE])
{
  return hmac(key, keylen, msg, msglen, sha1, HMAC_SHA1_BLOCKSIZE, output, HMAC_SHA1_OUTPUTSIZE);
}

/* 利用hmac_sha1函数实现PBKDF2_HMAC_SHA1算法 */
// https://en.wikipedia.org/wiki/PBKDF2
uint8_t pbkdf2_hmac_sha1(const void *password, uint16_t pwdlen, const void *salt, uint16_t saltlen, uint32_t c, uint8_t *dk, uint16_t dklen)
{
  uint8_t curr, k, *p;
  uint8_t ret;
  uint8_t u[3][HMAC_SHA1_OUTPUTSIZE];
  uint16_t i;
  uint32_t j;

  p = (uint8_t *)malloc(saltlen + 4);
  if (p == NULL)
    return 0;
  memcpy(p, salt, saltlen);
  memset(p + saltlen, 0, 2);

  for (i = 1; dklen; i++)
  {
    // INT_32_BE(i)
    p[saltlen + 2] = i >> 8;
    p[saltlen + 3] = i & 0xff;

    ret = hmac_sha1(password, pwdlen, p, saltlen + 4, u[1]); // 算出来的U1放到u[1]中
    if (!ret)
    {
      free(p);
      return 0;
    }
    memcpy(u[2], u[1], HMAC_SHA1_OUTPUTSIZE); // F=U1 (u[2]用来存放F)
    for (j = 2; j <= c; j++)
    {
      // F^=Uj -> F^=PRF(password, Uj-1)
      ret = hmac_sha1(password, pwdlen, u[1], HMAC_SHA1_OUTPUTSIZE, u[0]); // 根据Uj-1(位于u[1])算出Uj放到u[0]中
      if (!ret)
      {
        free(p);
        return 0;
      }
      for (k = 0; k < HMAC_SHA1_OUTPUTSIZE; k++)
      {
        u[2][k] ^= u[0][k];
        if (j != c)
          u[1][k] = u[0][k]; // 顺便把u[0]复制到u[1]中
      }
    }

    // u[2]为最终的结果F, 将其复制到结果缓冲区dk中
    curr = (dklen < HMAC_SHA1_OUTPUTSIZE) ? dklen : HMAC_SHA1_OUTPUTSIZE;
    memcpy(dk, u[2], curr);
    dk += curr;
    dklen -= curr;
  }

  free(p);
  return 1;
}

/* Pseudorandom function (PRF-n) */
// http://etutorials.org/Networking/802.11+security.+wi-fi+protected+access+and+802.11i/Part+II+The+Design+of+Wi-Fi+Security/Chapter+10.+WPA+and+RSN+Key+Hierarchy/Computing+the+Temporal+Keys/
// 参数n为PRF-n中的n除以8
uint8_t PRF(const void *k, uint16_t klen, const char *a, const void *b, uint16_t blen, void *output, uint8_t n)
{
  uint8_t alen = strlen(a);
  uint8_t buf[HMAC_SHA1_OUTPUTSIZE];
  uint8_t curr, i, *p, *q;

  p = (uint8_t *)malloc(alen + blen + 2);
  if (p == NULL)
    return 0;

  q = (uint8_t *)output;
  for (i = 0; n; i++)
  {
    strcpy((char *)p, a); // application-specific text (including '\0')
    memcpy(p + alen + 1, b, blen); // special data
    p[alen + blen + 1] = i; // single byte counter
    hmac_sha1(k, klen, p, alen + blen + 2, buf);

    curr = (n < HMAC_SHA1_OUTPUTSIZE) ? n : HMAC_SHA1_OUTPUTSIZE;
    memcpy(q, buf, curr);
    q += curr;
    n -= curr;
  }

  free(p);
  return 1;
}
WPA.h:
#define HMAC_MD5_BLOCKSIZE 64
#define HMAC_MD5_OUTPUTSIZE 16
#define HMAC_SHA1_BLOCKSIZE 64
#define HMAC_SHA1_OUTPUTSIZE 20

typedef void (*HashFunction)(unsigned char *input, int ilen, unsigned char *output);

void ARC4_decrypt_keydata(const uint8_t *KEK, const uint8_t *key_iv, const uint8_t *data, uint16_t datalen, uint8_t *output);
uint16_t AES_unwrap(const uint8_t *key, const uint8_t *data, uint16_t datalen, uint8_t *output);
uint8_t hmac(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, HashFunction hash, uint8_t blocksize, uint8_t *output, uint8_t outputsize);
uint8_t hmac_md5(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, uint8_t output[HMAC_MD5_OUTPUTSIZE]);
uint8_t hmac_sha1(const void *key, uint16_t keylen, const void *msg, uint16_t msglen, uint8_t output[HMAC_SHA1_OUTPUTSIZE]);
uint8_t pbkdf2_hmac_sha1(const void *password, uint16_t pwdlen, const void *salt, uint16_t saltlen, uint32_t c, uint8_t *dk, uint16_t dklen);
uint8_t PRF(const void *k, uint16_t klen, const char *a, const void *b, uint16_t blen, void *output, uint8_t n);

main.c:

#include <lwip/apps/httpd.h> // http服务器
#include <lwip/apps/netbiosns.h> // NetBIOS服务
#include <lwip/dhcp.h> // DHCP客户端
#include <lwip/dns.h> // DNS客户端
#include <lwip/init.h> // lwip_init函数所在的头文件
#include <lwip/timeouts.h> // sys_check_timeouts函数所在的头文件
#include <netif/ethernet.h> // ethernet_input函数所在头文件
#include <stm32f10x.h>
#include <string.h>
#include "common.h"
#include "WiFi.h"

// 这两个函数位于ethernetif.c中, 但没有头文件声明
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);

void dns_test(void);

static struct netif wifi_88w8686;
static uint8_t router_mac[6] = {0}; // 路由器MAC地址
#if LWIP_DHCP
static uint32_t dhcp_start_time = 0;
#endif

int fputc(int ch, FILE *fp)
{
  if (fp == stdout)
  {
    if (ch == '\n')
    {
      while ((USART1->SR & USART_SR_TXE) == 0); // 等待前一字符发送完毕
      USART1->DR = '\r';
    }
    while ((USART1->SR & USART_SR_TXE) == 0);
    USART1->DR = ch;
  }
  return ch;
}

/* 通知lwip网卡的连接状态 */
static void set_netif(struct netif *netif, uint8_t up)
{
  if (up)
  {
    netif_set_up(netif);
#if LWIP_DHCP
    dhcp_start(netif); // 路由器中显示的DHCP客户名称在ethernetif_init函数中设置
    dhcp_start_time = sys_now();
#endif
  }
  else
  {
    netif_set_down(netif);
#if LWIP_DHCP
    dhcp_stop(netif);
#endif
  }
}

/* WiFi认证成功回调函数 */
void WiFi_AuthenticationCompleteHandler(void)
{
  printf("Authenticated!\n");
  set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡
}

/* WiFi事件回调函数 */
void WiFi_EventHandler(const WiFi_Event *event)
{
  printf("[Event %d] size=%d", event->event_id, event->header.length);
  if (event->header.length >= sizeof(WiFi_Event) - sizeof(event->mac_addr))
    printf(", reason=%d", event->reason_code);
  if (event->header.length >= sizeof(WiFi_Event))
    printf(", MAC: %02X:%02X:%02X:%02X:%02X:%02X", event->mac_addr[0], event->mac_addr[1], event->mac_addr[2], event->mac_addr[3], event->mac_addr[4], event->mac_addr[5]);
  printf("\n");
  
  switch (event->event_id)
  {
    case 3:
      // 收不到信号 (例如和手机热点建立连接后, 把手机拿走), WiFi模块不会自动重连
      printf("Beacon Loss/Link Loss\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 4:
      // Ad-Hoc网络中不止1个结点, 且连接数发生了变化
      printf("The number of stations in this ad hoc newtork has changed!\n");
      break;
    case 8:
      // 认证已解除 (例如手机关闭了热点, 或者是连接路由器后一直没有输入密码而自动断开连接)
      printf("Deauthenticated!\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 9:
      // 解除了关联
      printf("Disassociated!\n");
      set_netif(&wifi_88w8686, 0);
      break;
    case 17:
      // Ad-Hoc网络中只剩本结点
      printf("All other stations have been away from this ad hoc network!\n");
      break;
    case 30:
      printf("IBSS coalescing process is finished and BSSID has changed!\n");
      break;
  }
  
  if (event->header.length > sizeof(WiFi_Event))
    dump_data(event + 1, event->header.length - sizeof(WiFi_Event));
}

/* WiFi模块收到新的数据帧 */
void WiFi_PacketHandler(const WiFi_DataRx *data)
{
  ethernetif_input(&wifi_88w8686); // 交给lwip处理
}

void associate_callback(void *arg, void *data, WiFi_Status status)
{
  switch (status)
  {
    case WIFI_STATUS_OK:
      printf("Associated!\n");
      set_netif(&wifi_88w8686, 1); // 在lwip中启用WiFi网卡
      break;
    case WIFI_STATUS_NOTFOUND:
      printf("SSID not found!\n");
      break;
    case WIFI_STATUS_FAIL:
      printf("Association failed!\n");
      break;
    case WIFI_STATUS_INPROGRESS:
      printf("Waiting for authentication!\n");
      break;
    default:
      printf("Unknown error! status=%d\n", status);
  }
}

void associate_example(void)
{
  WiFi_Connection conn;
  //WiFi_WEPKey wepkey = {0}; // 未使用的成员必须设为0
  conn.mac_addr = router_mac; // 用于接收热点的MAC地址, 断开连接时需要用到
  
  /*
  conn.security = WIFI_SECURITYTYPE_WEP;
  conn.ssid = "Oct1158-2";
  conn.password = &wepkey;
  wepkey.keys[1] = "1234567890123";
  wepkey.index = 1;
  WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_OPEN, -1, associate_callback, NULL); // 开放系统方式
  //WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_SHARED, -1, associate_callback, NULL); // 共享密钥方式
  */
  
  conn.security = WIFI_SECURITYTYPE_WPA; // WPA和WPA2都可以使用此选项
  conn.ssid = "Oct1158-2";
  conn.password = "helloworld"; // WPA密码直接指定, 不需要WiFi_WEPKey结构体
  WiFi_AssociateEx(&conn, WIFI_AUTH_MODE_OPEN, -1, associate_callback, NULL); // 必须使用WIFI_AUTH_MODE_OPEN选项
}

void adhoc_callback(void *arg, void *data, WiFi_Status status)
{
  if (status == WIFI_STATUS_OK)
  {
    printf("ADHOC %sed!\n", (char *)arg);
    set_netif(&wifi_88w8686, 1);
  }
  else
    printf("Cannot %s ADHOC!\n", (char *)arg);
}

void adhoc_example(void)
{
  WiFi_Connection conn;
  WiFi_WEPKey wepkey = {0}; // 未使用的成员必须设为0
  conn.mac_addr = NULL; // 不接收MAC地址
  
  /*
  conn.security = WIFI_SECURITYTYPE_WEP;
  conn.ssid = "Octopus-WEP";
  conn.password = &wepkey;
  wepkey.keys[1] = "3132333435";
  wepkey.index = 1; // 范围: 0~3
  WiFi_JoinADHOCEx(&conn, -1, adhoc_callback, "join");
  */
  
  ///*
  // 注意: 电脑上无论密码输入是否正确都可以连接, 但只有正确的密码才可以通信
  conn.security = WIFI_SECURITYTYPE_WEP;
  conn.ssid = "WM-G-MR-09";
  conn.password = &wepkey;
  wepkey.keys[3] = "1234567890123";
  wepkey.index = 3;
  WiFi_StartADHOCEx(&conn, adhoc_callback, "start");
  //*/
  
  /*
  conn.security = WIFI_SECURITYTYPE_NONE;
  conn.ssid = "Octopus-WEP";
  WiFi_JoinADHOCEx(&conn, -1, adhoc_callback, "join");
  */
  
  /*
  conn.security = WIFI_SECURITYTYPE_NONE;
  conn.ssid = "WM-G-MR-09";
  WiFi_StartADHOCEx(&conn, adhoc_callback, "start");
  */
}

void scan_callback(void *arg, void *data, WiFi_Status status)
{
  if (status == WIFI_STATUS_OK)
    printf("Scan finished!\n");
  else
    printf("Scan failed!\n");
  
  //adhoc_example();
  associate_example();
}

// 获取网卡MAC地址成功后, 就立即将网卡添加到lwip中, 但暂不把网卡设为"已连接"状态
void mac_address_callback(void *arg, void *data, WiFi_Status status)
{
#if !LWIP_DHCP
  struct ip4_addr ipaddr, netmask, gw;
#endif
  if (status == WIFI_STATUS_OK)
  {
    WiFi_Scan(scan_callback, NULL); // 扫描热点
    
    memcpy(wifi_88w8686.hwaddr, data, 6); // 将获得的MAC地址复制到全局变量中
    printf("MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n", wifi_88w8686.hwaddr[0], wifi_88w8686.hwaddr[1], wifi_88w8686.hwaddr[2], wifi_88w8686.hwaddr[3], wifi_88w8686.hwaddr[4], wifi_88w8686.hwaddr[5]);
    
#if LWIP_DHCP
    netif_add(&wifi_88w8686, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY, NULL, ethernetif_init, ethernet_input);
#else
    IP4_ADDR(&ipaddr, 192, 168, 1, 15); // IP地址
    IP4_ADDR(&netmask, 255, 255, 255, 0); // 子网掩码
    IP4_ADDR(&gw, 192, 168, 1, 1); // 网关
    netif_add(&wifi_88w8686, &ipaddr, &netmask, &gw, NULL, ethernetif_init, ethernet_input); // 添加WiFi模块到lwip中
#endif
    netif_set_default(&wifi_88w8686); // 设为默认网卡
  }
  else
    printf("Cannot get MAC address!\n");
}

void stop_callback(void *arg, void *data, WiFi_Status status)
{
  char *s1 = (char *)arg;
  char *s2 = (char *)s1 + strlen(s1) + 1;
  if (status == WIFI_STATUS_OK)
  {
    set_netif(&wifi_88w8686, 0);
    printf("%s %s!\n", s1, s2);
  }
  else
    printf("%s not %s!\n", s1, s2);
}

int main(void)
{
#if LWIP_DHCP
  struct dhcp *dhcp;
#endif
  uint8_t data;
  
  RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
  
  GPIOA->CRH = (GPIOA->CRH & 0xfffff00f) | 0x4b0; // 串口发送引脚PA9设为复用推挽输出(b), 串口接收引脚PA10设为浮空输入(4)
  
  USART1->BRR = SystemCoreClock / 115200;
  USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
  printf("STM32F103RE SDIO 88W8686\n");
  
  rtc_init();
  
  WiFi_Init();
  WiFi_GetMACAddress(mac_address_callback, NULL);
  
  lwip_init();
  
  netbiosns_init();
  netbiosns_set_name("STM32F103RE"); // 计算机名
  
  httpd_init();
  while (1)
  {
    if (SDIO->STA & SDIO_STA_SDIOIT)
    {
      SDIO->ICR = SDIO_ICR_SDIOITC;
      WiFi_Input();
    }
    else
      WiFi_CheckTimeout();
    
    sys_check_timeouts();
    
#if LWIP_DHCP
    if (dhcp_supplied_address(&wifi_88w8686))
    {
      if (dhcp_start_time != 0)
      {
        printf("DHCP supplied address at %.2fs!\n", (sys_now() - dhcp_start_time) / 1000.0);
        dhcp_start_time = 0;
        
        dhcp = netif_dhcp_data(&wifi_88w8686);
        printf("IP address: %s\n", ip4addr_ntoa(&dhcp->offered_ip_addr));
        printf("Subnet mask: %s\n", ip4addr_ntoa(&dhcp->offered_sn_mask));
        printf("Default gateway: %s\n", ip4addr_ntoa(&dhcp->offered_gw_addr));
#if LWIP_DNS
        printf("DNS Server: %s\n", ip4addr_ntoa(dns_getserver(0)));
        dns_test();
#endif
      }
    }
#endif
    
    // WiFi模块调试命令
    if (USART1->SR & USART_SR_RXNE)
    {
      data = USART1->DR;
      switch (data)
      {
        case 'd':
          WiFi_DiscardData();
          break;
        case 's':
          printf("SDIO->STA=0x%08x, ", SDIO->STA);
          printf("CARDSTATUS=%d, INTSTATUS=%d\n", WiFi_LowLevel_ReadReg(1, WIFI_CARDSTATUS), WiFi_LowLevel_ReadReg(1, WIFI_INTSTATUS));
          break;
        case 'A':
          // 离开ADHOC网络
          WiFi_StopADHOC(stop_callback, "ADHOC\0stopped"); // 如果当前未处于ADHOC模式, 则命令不会收到回应, 但最终回调函数肯定会调用
          break;
        case 'D':
          // 取消关联热点
          if (eth_addr_cmp((struct eth_addr *)router_mac, ðzero))
            printf("Unknown router MAC address!\n");
          else
            WiFi_Deauthenticate(router_mac, 3, stop_callback, "Connection\0closed"); // LEAVING_NETWORK_DEAUTH
          break;
      }
    }
  }
}

void HardFault_Handler(void)
{
  printf("Hard Error!\n");
  while (1);
}


dns_test.c:

#include <lwip/tcp.h>
#include <lwip/dns.h>

static err_t test_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
  printf("Connected! err=%d\n", err);
  err = tcp_close(tpcb);
  if (err == ERR_OK)
    printf("Connection is successfully closed!\n");
  else
    printf("Connection cannot be closed now! err=%d\n", err);
  return ERR_OK;
}

static void test_err(void *arg, err_t err)
{
  printf("Connection error! code=%d\n", err);
}

void connect_test(const ip_addr_t *ipaddr)
{
  struct tcp_pcb *tpcb;
  printf("Connecting to %s...\n", ip4addr_ntoa(ipaddr));
  tpcb = tcp_new();
  tcp_connect(tpcb, ipaddr, 80, test_connected);
  tcp_err(tpcb, test_err);
}

#if LWIP_DNS
static void dns_found(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
  if (ipaddr != NULL)
  {
    printf("DNS Found IP: %s\n", ip4addr_ntoa(ipaddr));
    connect_test(ipaddr);
  }
  else
    printf("DNS Not Found IP!\n");
}

void dns_test(void)
{
  ip_addr_t dnsip;
  err_t err = dns_gethostbyname("zh.arslanbar.net", &dnsip, dns_found, NULL);
  if (err == ERR_OK)
  {
    printf("In cache! IP: %s\n", ip4addr_ntoa(&dnsip));
    connect_test(&dnsip);
  }
  else
    printf("Not in cache! err=%d\n", err); // 缓存中没有时需要等待DNS解析完毕在dns_found回调函数中返回结果
}
#endif
common.c:
#include <stdio.h>
#include <stm32f10x.h>
#include "common.h"

#define RTC_USELSI // 因为板上没有LSE晶振, 所以RTC时钟选LSI

#ifdef RTC_USELSI
#define RTC_PRESCALER 40000
#else
#define RTC_PRESCALER 32768
#endif

void delay(uint16_t nms)
{
  uint32_t newtime = sys_now() + nms;
  while (sys_now() <= nms);
}

void dump_data(const void *data, uint32_t len)
{
  const uint8_t *p = data;
  while (len--)
    printf("%02X", *p++);
  printf("\n");
}

void rtc_init(void)
{
  RCC->APB1ENR |= RCC_APB1ENR_PWREN;
  PWR->CR |= PWR_CR_DBP; // 允许写后备寄存器(如RCC->BDCR)
  
#ifdef RTC_USELSI
  RCC->CSR |= RCC_CSR_LSION;
  while ((RCC->CSR & RCC_CSR_LSIRDY) == 0); // 等待LSI启动
#else
  if ((RCC->BDCR & RCC_BDCR_LSERDY) == 0)
  {
    RCC->BDCR |= RCC_BDCR_LSEON;
    while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0); // 等待LSE启动
  }
#endif
  
  // 若RTC未打开, 则初始化RTC
  if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0)
  {
    // 必须要先选择时钟, 然后再开启RTC时钟
#ifdef RTC_USELSI
    RCC->BDCR |= RCC_BDCR_RTCSEL_1 | RCC_BDCR_RTCEN; // 选LSI作为RTC时钟, 并开启RTC时钟, RTC开始走时
#else
    RCC->BDCR |= RCC_BDCR_RTCSEL_0 | RCC_BDCR_RTCEN;
#endif
    
    RTC->CRL |= RTC_CRL_CNF; // 进入RTC配置模式
    RTC->PRLH = 0; // 设置分频系数
    RTC->PRLL = RTC_PRESCALER - 1; // 定时1s, 注意PRLH/L寄存器只能写不能读
    //RTC->CNTH = 0; // 设置初始时间
    //RTC->CNTL = 50; // STM32F1系列的RTC没有年月日、小时等寄存器, 只有一个32位的计数器, 要想实现日期和时间的功能必须调用C库<time.h>中的mktime函数, 用软件来实现这些功能
    RTC->CRL &= ~RTC_CRL_CNF; // 保存设置
    while ((RTC->CRL & RTC_CRL_RTOFF) == 0); // 等待设置生效
  }
  else
  {
    // 等待RTC与APB1时钟同步
    RTC->CRL &= ~RTC_CRL_RSF;
    while ((RTC->CRL & RTC_CRL_RSF) == 0);
  }
}

// RTC时间转化为毫秒数
uint32_t sys_now(void)
{
  uint32_t sec = (RTC->CNTH << 16) | RTC->CNTL; // 秒
  uint32_t milli = (RTC_PRESCALER - RTC->DIVL - 1) * 1000 / RTC_PRESCALER; // 毫秒
  return sec * 1000 + milli;
}
common.h:
void delay(uint16_t nms);
void dump_data(const void *data, uint32_t len);
void rtc_init(void);
uint32_t sys_now(void);

lwip-2.0.3/include/arch/cc.h:

#define PACK_STRUCT_BEGIN __packed // struct前的__packed
lwip-2.0.3/include/lwipopts.h:
#define NO_SYS 1 // 无操作系统

#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define LWIP_STATS 0

#define MEM_ALIGNMENT 4 // STM32单片机是32位的单片机, 因此是4字节对齐的

#define SYS_LIGHTWEIGHT_PROT 0 // 不进行临界区保护

// 配置DHCP
#define LWIP_DHCP 1
#define LWIP_NETIF_HOSTNAME 1

// 配置DNS
#define LWIP_DNS 1
#define LWIP_RAND() ((u32_t)rand())

// WPA/WPA2认证需要用到lwip中的arc4, md5和sha1函数
// 需要修改各文件的第42行, 注释掉条件编译宏
#define LWIP_INCLUDED_POLARSSL_ARC4 1
#define LWIP_INCLUDED_POLARSSL_MD5 1
#define LWIP_INCLUDED_POLARSSL_SHA1 1
lwip-2.0.3/netif/ethernetif.c:
/**
 * @file
 * Ethernet Interface Skeleton
 *
 */

/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/*
 * This file is a skeleton for developing Ethernet network interface
 * drivers for lwIP. Add code to the low_level functions and do a
 * search-and-replace for the word "ethernetif" to replace it with
 * something that better describes your network interface.
 */

#include "lwip/opt.h"

//#if 0 /* don't build, this is only a skeleton, see previous comment */
#if 1 // 允许编译

#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "lwip/etharp.h"
#include "netif/ppp/pppoe.h"

// 包含头文件
#include "../../common.h"
#include "../../WiFi.h"

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

/**
 * Helper struct to hold private data used to operate your ethernet interface.
 * Keeping the ethernet address of the MAC in this struct is not necessary
 * as it is already kept in the struct netif.
 * But this is only an example, anyway...
 */
struct ethernetif {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

/* Forward declarations. */
/*static */void  ethernetif_input(struct netif *netif); // 必须去掉static

/**
 * In this function, the hardware should be initialized.
 * Called from ethernetif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
static void
low_level_init(struct netif *netif)
{
  //struct ethernetif *ethernetif = netif->state; // 无用的变量

  /* set MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
  //netif->hwaddr[0] = ;
  //...
  //netif->hwaddr[5] = ;
  // MAC地址已设置, 注释掉这段代码

  /* maximum transfer unit */
  netif->mtu = 1500;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

#if LWIP_IPV6 && LWIP_IPV6_MLD
  /*
   * For hardware/netifs that implement MAC filtering.
   * All-nodes link-local is handled by default, so we must let the hardware know
   * to allow multicast packets in.
   * Should set mld_mac_filter previously. */
  if (netif->mld_mac_filter != NULL) {
    ip6_addr_t ip6_allnodes_ll;
    ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
    netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
  }
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */

  /* Do whatever else is needed to initialize interface. */
}

/**
 * This function should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become available since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */

static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
  //struct ethernetif *ethernetif = netif->state; // 无用的变量
  //struct pbuf *q;
  uint8_t *buffer; // 添加此变量

  //initiate transfer();
#ifdef WIFI_DISPLAY_PACKET_SIZE
  printf("[Send] len=%hd\n", p->tot_len);
#endif

#if ETH_PAD_SIZE
  pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

  //for (q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
    //send data from(q->payload, q->len);
  //}
  buffer = WiFi_GetPacketBuffer(); // 获取发送缓冲区 (需要等待之前的帧发送完毕)
  pbuf_copy_partial(p, buffer, p->tot_len, 0); // 复制要发送的数据帧到发送缓冲区中
#ifdef WIFI_DISPLAY_PACKET_TX
  dump_data(buffer, p->tot_len);
#endif

  //signal that packet should be sent();
  WiFi_SendPacket(buffer, p->tot_len, NULL, NULL, WIFI_DEFAULT_TIMEOUT_DATAACK, WIFI_DEFAULT_MAXRETRY);

  MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
  if (((u8_t*)p->payload)[0] & 1) {
    /* broadcast or multicast packet*/
    MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
  } else {
    /* unicast packet */
    MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
  }
  /* increase ifoutdiscards or ifouterrors on error */

#if ETH_PAD_SIZE
  pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

  LINK_STATS_INC(link.xmit);

  return ERR_OK;
}

/**
 * Should allocate a pbuf and transfer the bytes of the incoming
 * packet from the interface into the pbuf.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return a pbuf filled with the received packet (including MAC header)
 *         NULL on memory error
 */
static struct pbuf *
low_level_input(struct netif *netif)
{
  //struct ethernetif *ethernetif = netif->state; // 无用的变量
  struct pbuf *p/*, *q*/;
  u16_t len;
  
  // 添加变量
  const uint8_t *data;

  /* Obtain the size of the packet and put it into the "len"
     variable. */
  //len = ;
  data = WiFi_GetReceivedPacket(&len); // 获取数据帧内容和大小
#ifdef WIFI_DISPLAY_PACKET_SIZE
  printf("[Recv] len=%hd\n", len);
#endif
#ifdef WIFI_DISPLAY_PACKET_RX
  dump_data(data, len);
#endif

#if ETH_PAD_SIZE
  len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif

  /* We allocate a pbuf chain of pbufs from the pool. */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);

  if (p != NULL) {

#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    //for (q = p; q != NULL; q = q->next) {
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable.
       * This does not necessarily have to be a memcpy, you can also preallocate
       * pbufs for a DMA-enabled MAC and after receiving truncate it to the
       * actually received size. In this case, ensure the tot_len member of the
       * pbuf is the sum of the chained pbuf len members.
       */
      //read data into(q->payload, q->len);
    //}
    pbuf_take(p, data, len); // 将数据帧内容复制到pbuf中
    //acknowledge that packet has been read();

    MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
    if (((u8_t*)p->payload)[0] & 1) {
      /* broadcast or multicast packet*/
      MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
    } else {
      /* unicast packet*/
      MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
    }
#if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif

    LINK_STATS_INC(link.recv);
  } else {
    //drop packet(); // 注释掉
    LINK_STATS_INC(link.memerr);
    LINK_STATS_INC(link.drop);
    MIB2_STATS_NETIF_INC(netif, ifindiscards);
  }

  return p;
}

/**
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
/*static */void // 必须去掉static
ethernetif_input(struct netif *netif)
{
  //struct ethernetif *ethernetif; // 无用的变量
  //struct eth_hdr *ethhdr;
  struct pbuf *p;

  //ethernetif = netif->state; // 注释掉

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* if no packet could be read, silently ignore this */
  if (p != NULL) {
    /* pass all packets to ethernet_input, which decides what packets it supports */
    if (netif->input(p, netif) != ERR_OK) {
      LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
      pbuf_free(p);
      p = NULL;
    }
  }
}

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 * This function should be passed as a parameter to netif_add().
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
err_t
ethernetif_init(struct netif *netif)
{
  struct ethernetif *ethernetif;

  LWIP_ASSERT("netif != NULL", (netif != NULL));

  ethernetif = mem_malloc(sizeof(struct ethernetif));
  if (ethernetif == NULL) {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
    return ERR_MEM;
  }

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "STM32F103RE_SDIO"; // 路由器中显示的名称
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);

  netif->state = ethernetif;
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
  netif->output = etharp_output;
#if LWIP_IPV6
  netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
  netif->linkoutput = low_level_output;

  ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);

  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}

#endif /* 0 */


评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值