该程序是旧版本!最新版本为20180706版:https://blog.csdn.net/ZLK1214/article/details/80941657本程序所用的单片机型号为:STM32F103REPB12端口为外接的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是否就是GTK2018年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
#include
#include
#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!
");
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
", 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!
");
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!
");
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
", 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
", 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
", sdio_func_num);
printf("Memory Present: %d
", (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
", 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
", 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
");
#else
SDIO->CLKCR = (SDIO->CLKCR & ~SDIO_CLKCR_CLKDIV) | 70; // 72MHz/(70+2)=1MHz
SDIO->DTIMER = 100000;
printf("SDIO Clock: 1MHz
");
#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
", 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!
");
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!
");
return 0;
}
}
WiFi.c:
#include
#include
#include
#include
#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!
");
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!
");
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
", 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!
");
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!
");
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!
");
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!
");
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...
", 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...
");
}
}
/* 检查发送缓冲区是否需要重传 */
// 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
", curr, len);
if (curr & 1)
{
// 若size为奇数(如17), 则说明接收端出现了CRC校验错误, 应重新传送上一次的内容(这部分代码省略)
printf("Error: an odd size is invalid!
");
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!
");
}
/* 丢弃收到但因程序错误一直未处理的数据或命令 */
void WiFi_DiscardData(void)
{
uint16_t len = WiFi_GetPacketLength();
WiFi_LowLevel_ReadData(1, wifi_port, NULL, len, 0);
printf("Discarded %d bytes!
", 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
", 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!
");
/* 生成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!
"); // 遇到内存不足的情况, 只需要把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!
");
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!
");
if (!WiFi_CheckMIC(packet_rx, data->rx_packet_length)) // prevents undetected modification of message 3
{
printf("Message 3 is corrupted!
");
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!
");
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
", 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!
");
break;
case 2:
printf("PTK & GTK set!
");
break;
case 3:
printf("PTK set!
");
break;
}
}
else if (header->type == WIFI_SDIOFRAME_DATA)
{
printf("Message %d sent!
", (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!
");
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!
");
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!
");
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
", 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
", 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
", 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!
");
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!
");
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!
");
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!
");
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!
");
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!
");
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!
");
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,