SDIO wifi Marvell8801/Marvell88w8801 介绍(六) ---- Marvell8801/Marvell88w8801实现初始化功能

在介绍SDIO wifi Marvell8801/Marvell88w8801之前先附上模块链接:点击购买Marvell8801开发板

代码工程的GITHUB连接:点进进入GITHUB仓库
https://github.com/sj15712795029/stm32f1_marvell88w8801_marvell8801_wifi

Marvell自己实现驱动系列文章分为几篇介绍:
SDIO wifi Marvell8801/Marvell88w8801 介绍(一) ---- 芯片介绍
SDIO wifi Marvell8801/Marvell88w8801 介绍(二) ---- SDIO协议介绍
SDIO wifi Marvell8801/Marvell88w8801 介绍(三) ---- 寄存器介绍
SDIO wifi Marvell8801/Marvell88w8801 介绍(四) ---- 命令/事件/数据格式
SDIO wifi Marvell8801/Marvell88w8801 介绍(五) ---- TLV
SDIO wifi Marvell8801/Marvell88w8801 介绍(六) ---- 实现初始化功能
SDIO wifi Marvell8801/Marvell88w8801 介绍(七) ---- 实现搜索功能
SDIO wifi Marvell8801/Marvell88w8801 介绍(八) ---- 实现STA功能
SDIO wifi Marvell8801/Marvell88w8801 介绍(九) ---- 实现AP功能
SDIO wifi Marvell8801/Marvell88w8801 介绍(十) ---- 移植TCP/IP协议栈LWIP
SDIO wifi Marvell8801/Marvell88w8801 介绍(十一) ---- 自己编写LWIP没有的DHCP server
SDIO wifi Marvell8801/Marvell88w8801 介绍(十二) ---- MQTT介绍
SDIO wifi Marvell8801/Marvell88w8801 介绍(十三) ---- 百度云操作说明
SDIO wifi Marvell8801/Marvell88w8801 介绍(十四) ---- 上位机STA操作/代码
SDIO wifi Marvell8801/Marvell88w8801 介绍(十五) ---- 上位机AP操作/代码
SDIO wifi Marvell8801/Marvell88w8801 介绍(十六) ---- 上位机UDP操作/代码
SDIO wifi Marvell8801/Marvell88w8801 介绍(十七) ---- 上位机TCP操作/代码
SDIO wifi Marvell8801/Marvell88w8801 介绍(十八) ---- 上位机PING操作/代码
SDIO wifi Marvell8801/Marvell88w8801 介绍(十九) ---- 上位机云服务器(百度云)操作/代码

每篇更新打开专栏可以看到打开Marvell8801/Marvell8801 专栏

1. Marvell88w8801 IOPORT

Marvell的IO PORT分为两种,第一种是control io port,主要用于传输cmd/cmd response/event,另外fw也是通过这个io port写下去的,这个是固定的,另外一种是data port,用于传输上行和下行数据,read是1~16,write是根据get hw spec的命令响应得到的,关于data port在在后面lwip移植的时候再做说明,此部分只是说control io port.
Control io port你可以简单的理解为wifi的寄存器地址,cmd/fw data都是往这个地址去写,cmd response是通过这个地址读回来,要想得到这个control io port需要通过读几个寄存器得到这个,也就是前面文章介绍寄存器的时候说的:
在这里插入图片描述
整个程序处理如下:

/******************************************************************************
 *	函数名:	mrvl88w8801_get_control_io_port
 * 参数:  	NULL
 * 返回值: 	返回执行结果
 * 描述:	通过IO_PORT_0_REG IO_PORT_1_REG IO_PORT_2_REG得到
 			control io port(也就是cmd/cmd resp/fw data的读写寄存器地址)
******************************************************************************/
static uint8_t mrvl88w8801_get_control_io_port()
{
    uint32_t control_io_port = 0;
    uint8_t temp_op_port = 0;;

    hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,IO_PORT_0_REG,0,&temp_op_port);
    control_io_port |= temp_op_port;
    hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,IO_PORT_1_REG,0,&temp_op_port);
    control_io_port |= (temp_op_port<<8);
    hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,IO_PORT_2_REG,0,&temp_op_port);
    control_io_port |= (temp_op_port<<16);

    COMP_DEBUG("WIFI:io_port 0x%x\n",control_io_port);
    pmrvl88w8801_core->control_io_port = control_io_port;
    return COMP_ERR_OK;

}

此程序比较简单,就是通过SDIO cmd52把IO_PORT_0_REG IO_PORT_1_REG IO_PORT_2_REG的寄存器地址值读出来,组合起来就得到control io port,并且存储到pmarvell88w8801_core的全局变量成员control_io_port中,后续 download fw/cmd/cmd response都会用到这个

2. Marvell88w8801 download fw

Download fw的过程在说明Linux driver的时候整个过程说过,我这部分在哪个基础做了一个简单的,主要实现在mrvl88w8801_download_fw这个函数中
一共有以下几个step:
Step 1: 首先把fw的二进制文件转换为数组,转换成的样子如下,截图是部分fw,完整的请参照代码
在这里插入图片描述
Step 2:把数组长度和数组赋值给一个变量
在这里插入图片描述
Step 3: polling card status
在这里插入图片描述

/******************************************************************************
 *	函数名:	mrvl88w8801_download_fw_wait
 * 参数:  	NULL
 * 返回值: 	返回执行结果
 * 描述:	polling card status,读取寄存器CARD_TO_HOST_EVENT_REG 0x30的状态
 			判断bit0,bit3是否都被置位
******************************************************************************/
static uint8_t mrvl88w8801_download_fw_wait()
{
    uint8_t status = 0;;
    while(1)
    {
        hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,CARD_TO_HOST_EVENT_REG,0,&status);
        if((status & CARD_IO_READY) == CARD_IO_READY && (status & DN_LD_CARD_RDY) == DN_LD_CARD_RDY)
            break;
    }

    return COMP_ERR_OK;
}

Step 4: Get next block len
在这里插入图片描述

 /* Get next block length */
hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,READ_BASE_0_REG,0,&fw_req_temp_size);
fw_req_size |= fw_req_temp_size;
hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,READ_BASE_1_REG,0,&fw_req_temp_size);
fw_req_size |= (fw_req_temp_size<<8);

if(fw_req_size == 0)
    continue;

//COMP_DEBUG("WIFI:Required: %d bytes, Remaining: %d bytes\r\n", fw_req_size, fw_len);
if (fw_req_size & 1)
{
    /* 若size为奇数(如17)则说明接收方出现了CRC校验错误 */
    COMP_DEBUG("WIFI:Error: an odd size is invalid!\n");
    return WIFI_ERR_REQ_INVALID_FW_SIZE;
}

Step 5: Write Next Block
在这里插入图片描述

hw_sdio_cmd53(SDIO_EXCU_WRITE,SDIO_FUNC_1,control_io_port,0,(uint8_t *)fw_data,fw_req_size);

Step 6:firmware active check
在这里插入图片描述

/* Check firmware active */
for(index = 0; index < WIFI_CONF_MAX_POLL_TRIES; index++)
{
   mrvl88w8801_get_fw_status(&fw_status);
   if (fw_status == FIRMWARE_READY)
   {
       COMP_DEBUG("WIFI:fw is active index %d\n",index);
       return COMP_ERR_OK;
   }
}

整个流程看mrvl88w8801_download_fw这个函数,流程很容易看懂,完整的api代码如下:

/******************************************************************************
 *	函数名:	mrvl88w8801_download_fw
 * 参数:  	NULL
 * 返回值: 	返回执行结果
 * 描述:	下载firmware到芯片
******************************************************************************/
static uint8_t mrvl88w8801_download_fw()
{
    uint16_t index;
    uint16_t fw_status;
    const uint8_t *fw_data = mrvl88w8801_fw;
    uint32_t fw_len = sizeof(mrvl88w8801_fw);
    uint16_t fw_req_size = 0;
    uint8_t fw_req_temp_size = 0;
    uint32_t control_io_port = pmrvl88w8801_core->control_io_port;

#if 0
    /* Check firmware active */
    for(index = 0; index < WIFI_CONF_MIN_POLL_TRIES; index++)
    {
        mrvl88w8801_get_fw_status(&fw_status);
        if (fw_status == FIRMWARE_READY)
        {
            COMP_DEBUG("WIFI:fw is active index %d\n",index);
            return COMP_ERR_OK;
        }
    }
#endif

    while (fw_len)
    {
        fw_req_size = 0;
        fw_req_temp_size = 0;
        if(fw_len != sizeof(mrvl88w8801_fw))
        {
            /* polling card status */
            mrvl88w8801_download_fw_wait();
        }

        /* Get next block length */
        hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,READ_BASE_0_REG,0,&fw_req_temp_size);
        fw_req_size |= fw_req_temp_size;
        hw_sdio_cmd52(SDIO_EXCU_READ,SDIO_FUNC_1,READ_BASE_1_REG,0,&fw_req_temp_size);
        fw_req_size |= (fw_req_temp_size<<8);

        if(fw_req_size == 0)
            continue;

        //COMP_DEBUG("WIFI:Required: %d bytes, Remaining: %d bytes\r\n", fw_req_size, fw_len);
        if (fw_req_size & 1)
        {
            /* 若size为奇数(如17)则说明接收方出现了CRC校验错误 */
            COMP_DEBUG("WIFI:Error: an odd size is invalid!\n");
            return WIFI_ERR_REQ_INVALID_FW_SIZE;
        }

        if (fw_req_size > fw_len)
            fw_req_size = fw_len;

        /* Write block */
        hw_sdio_cmd53(SDIO_EXCU_WRITE,SDIO_FUNC_1,control_io_port,0,(uint8_t *)fw_data,fw_req_size);

        fw_len -= fw_req_size;
        fw_data += fw_req_size;
    }

    /* Check firmware active */
    for(index = 0; index < WIFI_CONF_MAX_POLL_TRIES; index++)
    {
        mrvl88w8801_get_fw_status(&fw_status);
        if (fw_status == FIRMWARE_READY)
        {
            COMP_DEBUG("WIFI:fw is active index %d\n",index);
            return COMP_ERR_OK;
        }
    }

    return WIFI_ERR_INVALID_FW_STATUS;

}

3. Marvell88w8801 init cmd

此部分和Linux还有差异,主要是精简版的,比Linux少了很多命令
下面直接来看下所有的CMD
Step 1:发送HostCmd_CMD_FUNC_INIT 0x00a9命令和处理cmd response
首先来看下HostCmd_CMD_FUNC_INIT 0x00a9的命令
在这里插入图片描述
1)发送HostCmd_CMD_FUNC_INIT 0x00a9


/******************************************************************************
 *	函数名:	mrvl88w8801_func_init
 * 参数:  	tx(IN)				-->tx buffer
 * 返回值: 	返回执行结果
 * 描述:	组HostCmd_CMD_FUNC_INIT command的封包
******************************************************************************/
static uint8_t mrvl88w8801_func_init(uint8_t* tx)
{
    uint16_t tx_packet_len = CMD_HDR_SIZE;
    HostCmd_DS_COMMAND *cmd = (HostCmd_DS_COMMAND *)tx;

    cmd->pack_len = tx_packet_len;
    cmd->pack_type = TYPE_CMD_CMDRSP;
    cmd->command = HostCmd_CMD_FUNC_INIT;
    cmd->size = tx_packet_len - CMD_SDIO_HDR_SIZE;
    cmd->seq_num = 0;
    cmd->bss = 0;
    cmd->result = 0;

    return COMP_ERR_OK;
}

然后通过CMD53发送出去

hw_sdio_cmd53(SDIO_EXCU_WRITE,SDIO_FUNC_1,pmrvl88w8801_core->control_io_port +CTRL_PORT,0,tx,tx_len);

2)处理cmd response
mrvl88w8801_process_cmdrsp中case HostCmd_CMD_FUNC_INIT然后发送下一条cmd
HostCmd_CMD_MAC_CONTROL
在这里插入图片描述
Step 2: 发送HostCmd_CMD_MAC_CONTROL 0x0028命令和处理cmd response
首先来看下HostCmd_CMD_MAC_CONTROL 0x0028的命令
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
另外,补充一点,此文档和Linux代码有出入,文档bit 0~bit2 是reserved,但是驱动中是这样的,也就是说bit0是RX on,bit 1是TX on,此部分要注意
在这里插入图片描述
1)发送HostCmd_CMD_MAC_CONTROL 0x0028命令

/******************************************************************************
 *	函数名:	mrvl88w8801_mac_control
 * 参数:  	tx(IN)				-->tx buffer
 			data_buff(IN)		-->action行为指针
 * 返回值: 	返回执行结果
 * 描述:	组HostCmd_CMD_MAC_CONTROL command的封包
******************************************************************************/
static uint8_t mrvl88w8801_mac_control(uint8_t* tx,void *data_buff)
{
    uint16_t action = *((uint16_t *) data_buff);
    HostCmd_DS_COMMAND *cmd = (HostCmd_DS_COMMAND *)tx;
    HostCmd_DS_MAC_CONTROL *pmac = &cmd->params.mac_ctrl;
    uint16_t tx_packet_len = CMD_HDR_SIZE + sizeof(HostCmd_DS_MAC_CONTROL);

    cmd->pack_len = tx_packet_len;
    cmd->pack_type = TYPE_CMD_CMDRSP;
    cmd->command = HostCmd_CMD_MAC_CONTROL;
    cmd->size = tx_packet_len - CMD_SDIO_HDR_SIZE;
    cmd->seq_num = 0;
    cmd->bss = 0;
    cmd->result = 0;
    pmac->action = comp_cpu_to_le16(action);

    return COMP_ERR_OK;
}

然后通过CMD53发送出去
在这里插入图片描述
2)处理HostCmd_CMD_MAC_CONTROL 0x0028 cmd response
mrvl88w8801_process_cmdrsp中case HostCmd_CMD_MAC_CONTROL然后发送下一条cmd
HostCmd_CMD_GET_HW_SPEC
在这里插入图片描述

Step 3: 发送HostCmd_CMD_GET_HW_SPECL 0x0003命令和处理cmd response
首先来看下HostCmd_CMD_GET_HW_SPECL 0x0003的命令
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1)发送HostCmd_CMD_GET_HW_SPECL 0x0003命令

/******************************************************************************
 *	函数名:	mrvl88w8801_get_hw_spec
 * 参数:  	tx(IN)				-->tx buffer
 * 返回值: 	返回执行结果
 * 描述:	组HostCmd_CMD_GET_HW_SPEC command的封包
******************************************************************************/
static uint8_t mrvl88w8801_get_hw_spec(uint8_t* tx)
{
    HostCmd_DS_COMMAND *cmd = (HostCmd_DS_COMMAND *)tx;
    uint16_t tx_packet_len = CMD_HDR_SIZE + sizeof(HostCmd_DS_GET_HW_SPEC);

    cmd->pack_len = tx_packet_len;
    cmd->pack_type = TYPE_CMD_CMDRSP;
    cmd->command = HostCmd_CMD_GET_HW_SPEC;
    cmd->size = tx_packet_len - CMD_SDIO_HDR_SIZE;
    cmd->seq_num = 0;
    cmd->bss = 0;
    cmd->result = 0;

    return COMP_ERR_OK;
}

然后通过CMD53发送出去
在这里插入图片描述
2)处理HostCmd_CMD_GET_HW_SPECL 0x0003 cmd response
此部分的cmd response的处理方式和上面两个cmd还有差异,上面两个cmd是收到response就继续下发下一条cmd,但是这个需要解析get hw spec的命令,主要是用于拿到2.1小节我们说的write data port的最大值
在这里插入图片描述
在这里插入图片描述
另外,我们在发送HostCmd_CMD_802_11_MAC_ADDRESS 0x004d的cmd
Step 4: 发送HostCmd_CMD_802_11_MAC_ADDRESS 0x004d命令和处理cmd response
首先来看下HostCmd_CMD_802_11_MAC_ADDRESS 0x004d的命令
在这里插入图片描述
在这里插入图片描述
1)发送HostCmd_CMD_802_11_MAC_ADDRESS 0x004d命令

/******************************************************************************
 *	函数名:	mrvl88w8801_mac_addr_prepare
 * 参数:  	tx(IN)				-->tx buffer
 			cmd_action(IN)	-->set/get
 			data_buff(IN)		-->mac address,此部分主要用于set
 			data_len(IN)		-->mac的长度
 * 返回值: 	返回执行结果
 * 描述:	组HostCmd_CMD_802_11_MAC_ADDRESS command的封包
******************************************************************************/
static uint8_t mrvl88w8801_mac_addr_prepare(uint8_t* tx,uint16_t cmd_action,void *data_buff,uint16_t data_len)
{
    HostCmd_DS_COMMAND *cmd = (HostCmd_DS_COMMAND *)tx;
    HostCmd_DS_802_11_MAC_ADDRESS *pmac_addr = &cmd->params.mac_addr;
    uint16_t tx_packet_len = CMD_HDR_SIZE + sizeof(HostCmd_DS_802_11_MAC_ADDRESS)+data_len;

    cmd->pack_len = tx_packet_len;
    cmd->pack_type = TYPE_CMD_CMDRSP;
    cmd->command = HostCmd_CMD_802_11_MAC_ADDRESS;
    cmd->size = tx_packet_len - CMD_SDIO_HDR_SIZE;
    cmd->seq_num = 0;
    cmd->bss = 0;
    cmd->result = 0;
    if(cmd_action == HostCmd_ACT_GEN_GET)
    {
        pmac_addr->action = HostCmd_ACT_GEN_GET;
        comp_memset(pmac_addr->mac_addr,0,MAC_ADDR_LENGTH);
    }
    else	/* set mac address */
    {
        pmac_addr->action = HostCmd_ACT_GEN_SET;
        comp_memcpy(pmac_addr->mac_addr,data_buff,MAC_ADDR_LENGTH);
    }
    return COMP_ERR_OK;
}

2)处理HostCmd_CMD_802_11_MAC_ADDRESS 0x004d cmd response
此部分只有一个cmd response的解析
在这里插入图片描述


/******************************************************************************
 *	函数名:	mrvl88w8801_ret_mac_address
 * 参数:  	rx_buffer(IN)			-->rx buffer
 			len(IN)				-->rx buffer len
 * 返回值: 	返回执行结果
 * 描述:	此部分主要是处理mac cmd reponse,如果拿到mac,那么会做两个事情:
 			1.更新pmrvl88w8801_core结构体中断额mac_address
 			2.初始化lwip的mac
******************************************************************************/
static uint8_t mrvl88w8801_ret_mac_address(uint8_t *rx_buffer,int len)
{
    HostCmd_DS_802_11_MAC_ADDRESS *pconnect_rsp = (HostCmd_DS_802_11_MAC_ADDRESS *)rx_buffer;
    comp_memcpy(pmrvl88w8801_core->mac_address,pconnect_rsp->mac_addr,MAC_ADDR_LENGTH);
    COMP_DEBUG("mrvl88w8801_ret_mac_address mac dump\n");

    ethernet_sta_driver_init(pmrvl88w8801_core->mac_address);

    if(mrvl_wifi_cb && mrvl_wifi_cb->wifi_init_result)
        mrvl_wifi_cb->wifi_init_result(COMP_ERR_OK);
    return COMP_ERR_OK;
}

注释说的很清楚了,至于初始化lwip的mac部分我们后续再做说明,到此,我们init的过程已经完结,相比于Linux,我们做的精简,而且易懂,你再配合这code看,会有种大彻大悟的感觉

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Wireless_Link

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

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

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

打赏作者

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

抵扣说明:

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

余额充值