【程序】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 <[email protected]>
 *
 */

/*
 * 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 */


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章