【程序】Marvell 88W8686 WiFi模块(WM-G-MR-09)创建或连接热点,并使用l

2019-07-14 03:39发布

该程序是旧版本!最新版本为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,