DFU工作过程中USB机制

时间:2022-11-16 15:29:49

在一级bootloader执行进入USB启动方式之后,设备进行枚举。枚举过程中会通过PC端发送命令对连接的USB设备进行枚举。当枚举成功之后,在PC端可以看到设备的盘符。

当设备能够被PC正确识别之后。接下来就能够通过烧写工具完毕设备的扫描假设成功找到设备,则能够通过USB数据传输到SRAM中,这时候的数据主要包含2k infor文件。一级bootloader在成功的解析2kinfor 之后。PC端会将DFU文件传输到初始化好的DRAM中。而且PC指针跳转到DFU地址处运行,运行过程中会跳转到USB初始化程序过程中。

USB的初始化过程主要分为两个部分,一个部分是初始化UDC设备。第二部分是等待PC端发送的命令并响应。

这个过程的描写叙述例如以下:

在USB的初始化过程中会调用VIM_CLKRST_UotgExitRst();函数。

此函数中,

VOID VIM_CLKRST_UotgExitRst(VOID)

{

__CLKRST_UotgSWRst();//为了防止global rst之后。来不了中断导致挂死

__CLKRST_UotgExitRst();

//waitfor uotg swrstdone irq

while(!__CLKRST_CheckUotgSwrst())//poolling

{

//donothing

};

__CLKRST_ClearUotgSwrstIrq();

}

会首先设置0x604寄存器的bit25。UOTG_SW_RESET。

接着设置0x610寄存器的SW_CFG_UOTG_DONE,bit12。uotg software configuration done flag, high active

然后死等UOTG_RSTDONE_IRQ中断的到来。最后写清中断。

在初始化函数的最后,会调用

UDC_DFU_Init(VIM_USB_DataProcess,VIM_USB_ArmSpeedBinProcess);函数。函数中会将两个函数指针进行赋值给全局变量g_Usb_Process_Call以及g_Usb_SpeedBin_Call。函数指针的定义为typedef VIM_RESULT(* PFatCallBack)(UINT8 *, UINT32);

void UDC_DFU_Init(PFatCallBackp_data_process,PFatCallBack p_speedbin_process)

{

VIM_HAL_PrintStr("UDC_DFU_Init:\r\n");

g_Usb_Process_Call= p_data_process;//处理收到的512字节,就是一个sector

//当中g_Usb_Process_Call用于USB工具下载IMAGE时对一个sector的处理

g_Usb_SpeedBin_Call= p_speedbin_process;//装载speed sorting镜像

g_FatCtx.pcall= p_data_process;

//g_FatCtx.pcall用于U盘拷贝方式对一个sector的处理

g_FatCtx.fat_total_sec= ChkFatBoot(DFU_SIZE);

udc_mass_init(&g_DfuMedOp, 1);

udc_mass_start();

}

此函数中会对fat设备的cluster等变量值进行计算,而且初始化mass设备。初始化的过程主要是对设备操作过程中记录状态机等信息的全局变量this_usb_udc进行赋初始值。

void udc_mass_init(PUSB_MED_IF pMedIf,UINT8 lun_num)

{

VIM_HAL_Memset(&g_mass_ctx,0, sizeof(g_mass_ctx));

g_mass_ctx.maxlun= 0;

//     g_DataShared= (UINT8*)VIM_USB_DATA_BUF;

g_mass_ctx.lun[0].dma_addr[0]= (void *) (((UINT32)g_DmaNotMallocAddr+0x1000)&0xfffff000);

g_mass_ctx.lun[0].pUmdIf= pMedIf;

udc_init();

}

在udc_init()中会完毕对设备赋初值的操作。主要包含设备的初始状态值,DMA地址等信息以及例如以下结构体变量值。

USB_MED_IF g_DfuMedOp =

{

NULL,

MedBoot_rd_sec,

MedBoot_wr_sec,

MedBoot_get_status,

MedBoot_get_sec_num,

NULL,

{

{'o','o', 'o', 'o', 'o', 'o', 'o', 0x20},//VID

{'S','U', 'P', 'E', 'R', 0x20, 'D', 'I', 'S', 'K', 0x20, 0x20, 0x20, 0x20, 0x20,0x20},//PID

{'1','.','0','0'}//VERSIIN

}

};

在整个初始化过程的最后会通过配置寄存器连接并使能设备为快速设备。

Udc_SetUsbPower(UDC_M_POWER_SOFTCONN|UDC_M_POWER_HSENAB);

至此设备的初始化过程完毕,接下来会进入循环等待设备与host端进行交互的操作过程。

在udc_mass_process函数中会注冊“查询处理usb传输数据状态。进行数据处理”的中断,接受中断并进行处理。

void udc_mass_process(void)

{

udc_int_usb();//查询处理usb传输数据状态。进行数据处理

if(g_mass_ctx.usb_state!= USB_STATE_CONFIGURED)

return;

if(g_mass_ctx.umb_state== UMB_IDLE)

{

req_cbw();

}

elseif(g_mass_ctx.umb_state == UMB_CBW_OK)

{

do_cbw();

}

}

在设备接收到的CBW状态为UMB_CBW_OK时,会通过do_cbw对数据进行分发和处理。这个过程中。主要是通过UmscProcess函数。

在case中会看到在PC端程序中传递下来的子命令。

static void do_cbw(void)

{

UINT32  reqlen = 0;

pCsw->Tag= pCbw->Tag;

reqlen= UdcGet32Lbuf( (UINT8*)(&pCbw->TransferLength) );

g_mass_ctx.umb_state= UMB_CBW_PROCESS;

UmscProcess(&g_mass_ctx.lun[pCbw->Resv_Lun], pCbw->CbOpt, pCbw->CbData,pCbw->Flags, reqlen );

}

依据上面的描写叙述大体上了解了USB设备注冊完毕之后设备的一些状态问题。在上面的描写叙述中可以看到打开了USB的中断。这时候设备会进行又一次枚举的过程,又一次枚举的过程是会首先发送reset命令。在DFU中等待设备reset的过程显得比較漫长。这个过程中须要注意是否可以进行优化。

此处还是没有查明究竟reset是从代码的那个位置传送下来,还是在udc_mass_process完毕注冊之后会一直扫描中断,可是后者看起来不像。

当接收到reset中断后。会对端点进行又一次设置,而且通过调用udc_nuke_req()函数完毕实际的操作。

/***************************************************************************************

总线RESET后运行的操作

****************************************************************************************/

static void udc_usbreset(void)

{

UINT32val;

UINT8 i;

VIM_DBG_Print(USB_MSG_DEVICE_RESET);

//Udc_SetIntUnmask(UDC_INT_USB|UDC_INT_DMA);

//usbint , all without sof

val= ~UDC_M_INTR_SOF;

Udc_SetUsbIrqEn(val);

Udc_SetInIrqEn((0x1<<EP_CTRL)|(0x1<<EP_BULK_IN)|(0x1<<EP_INTR_IN) );

Udc_SetOutIrqEn((0x1<<EP_CTRL)|(0x1<<EP_BULK_OUT)|(0x1<<EP_INTR_OUT) );//又一次使能对应的端点

this_usb_udc.state= USB_STATE_DEFAULT;

this_usb_udc.highspeed= 1;

//各个端点注冊的RESET函数,在udc_ttc.c和udc_bulkonly中定义了this_usb_udc.driver指向的实体结构体

//g_ttc_driver      and   g_mass_driver

if(this_usb_udc.driver != NULL )//在TTC中,将命令的各个管道的USBstate设置成usb_state =USB_STATE_DEFAULT

this_usb_udc.driver->reset();

udc_nuke_req();//??

????

??

?????????

?

???????

???

??

???

??

??

??

??

?

//OUT和中断端点设置BUSY位。为什么?

这个BUSY的用处是什么?

//这两位为1表示不能使能RX端点,即清除它的RXPKTRDY位。不能接收下笔数据

//这两位在第一次REQ_CBW时被清除。因为系统默认是能够接收数据的,这次就不必

//人工使能端点接收了

this_usb_udc.ep[EP_BULK_OUT].busy= 1;//此时g_ep_out=EP_IDX_1

this_usb_udc.ep[EP_INTR_OUT].busy= 1;//此时EP_INTR_OUT=EP_IDX_3

//清除端点的FIFO和TOG。释放停止状态标志

for(i=0;i<EP_GT_NUM; i++)

{

Udc_EpRst(i, USB_EP_RET_DIR_OUT);//out

Udc_EpRst(i,USB_EP_RET_DIR_IN); //in

this_usb_udc.ep[i].halt= 0;

}

}

上面的代码中调用了this_usb_udc.driver->reset();这个函数的赋值是通过对结构体进行总体赋值完毕的,

struct usb_driver g_mass_driver =

{

UsbMassClsReq,

Usb_MassGetDescBuf,

UsbMassSetCfg,

UsbMassReset,

UsbMassSuspend,

UsbMassSetIntf

};

usb_driver结构体的定义为:

struct usb_driver {

void                                    (*udc_vendor_class_req)(structusb_fifo *);

UINT8*         (*get_desc)(UINT8 highspeed, UINT8type, UINT16* len, UINT8 strIdx);

void                                    (*set_cfg)(void);

void                                    (*reset)(void);

void                                    (*suspend)(void);

void                                    (*set_interface)(UINT16intface, UINT16 altset);

};

实际过程中UsbMassReset函数完毕的操作不过对g_mass_ctx.usb_state进行赋值为 USB_STATE_DEFAULT,设置这个状态会让设备。

眼下可以看到的在DFU中状态机的操作是在GetConfig的过程中作为控制端点是否可用的状态推断符,

if(this_usb_udc.state == USB_STATE_DEFAULT )

{

udc_CtrlCmdNotSupport();

return;

}

假设不支持这种控制命令那么就会stall endpoint为停止状态。

在udc_nuke_req()函数中。会依据选择的传输方式不同进行不同的控制。通常情况下,控制传输使用FIFO方式,而在须要大量传输数据的bulk传输过程中会使用DMA方式。