最近因为项目的原因看了下DFU的资料,也下个Demo来研究了下,其实也就是在USB驱动中嵌入DFU驱动而已,弄明白了DFU的协议,就很容易了。DFU有个specification,全名叫USB Device Class Specification for Device Firmware Upgrade。可见其和USB之间的密切关系。
当把DFU嵌入USB后,系统运行状态一般就可以分为二种模式,一种是就是正常的USB运行模式,一种是DFU模式,二种模式不能共存。那如果确定是哪种模式呢,这就要通过寻找DFU class interface descriptor来判断设备是否具有DFU功能,也就是升级固件的功能。而这个DFU class interface descriptor和USB中interface中的interface descriptor结构是一样的,的在DFU Specification中提到其interface descriptor中的bInterfaceClass为EFh,而其bInterfaceSubClass为01h,这就是我们判断的依据。
if (intf->bInterfaceClass == 0xfe && intf->bInterfaceSubClass == 1)
{
......
}
这里需要注意的是,在正常的模式下,只有一个DFU Class interface descriptor.同时在bInterfaceProtocol为01的话就代表还是在正常模式,为02在处于DFU协议下了。
如果有多个设备具有DFU功能,这时就要注意了,因为在 DFU Specification中提到,其协议只支持一个具有DFU功能的设备,原因是在从正常模式下进入DFU模式前必须reset总线。具有DFU功能的设备地址将会改变。
发送dfu_detach后,还重新re-scan USB。这里的dfu_detach是DFU Specification中定义的请求,如果函数成功则可以在reset usb后进入DFU模式,下面是发送DFU的代码,利用usblib这个库,也就是我在上一篇提到的。
usb_control_msg( device,
/* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
/* bRequest */ DFU_DETACH,
/* wValue */ timeout,
/* wIndex */ interface,
/* Data */ NULL,
/* wLength */ 0,
dfu_timeout );
在DFU Specification中提到DFU有很多状态,在各个状态实现不同的功能,比如在 DFU_STATE_dfuERROR状态下只能调用dfu_clear_status才能回到dfuIdle模式下,Demo中的代码如下:
status_again:
printf("Determining device status: ");
if (dfu_get_status(dif->dev_handle, dif->interface, &status ) < 0) {
fprintf(stderr, "error get_status: %s/n", usb_strerror());
exit(1);
}
switch (status.bState) {
case DFU_STATE_appIDLE:
case DFU_STATE_appDETACH:
fprintf(stderr, "Device still in Runtime Mode!/n");
exit(1);
break;
case DFU_STATE_dfuERROR:
printf("dfuERROR, clearing status/n");
if (dfu_clear_status(dif->dev_handle, dif->interface) < 0) {
fprintf(stderr, "error clear_status: %s/n", usb_strerror());
exit(1);
}
goto status_again;
break;
case DFU_STATE_dfuDNLOAD_IDLE:
case DFU_STATE_dfuUPLOAD_IDLE:
printf("aborting previous incomplete transfer/n");
if (dfu_abort(dif->dev_handle, dif->interface) < 0) {
fprintf(stderr, "can't send DFU_ABORT: %s/n", usb_strerror());
exit(1);
}
goto status_again;
break;
case DFU_STATE_dfuIDLE:
printf("dfuIDLE, continuing/n");
break;
}
其中的dfu_get_status等都是调用usb_control_msg来实现的。
然后可以执行下载,上传的操作了。同样调用usb_control_msg来实现,比如对于更新固件,就可以
status = usb_control_msg( device,
/* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
/* bRequest */ DFU_UPLOAD,
/* wValue */ transaction++,
/* wIndex */ interface,
/* Data */ data,
/* wLength */ length,
dfu_timeout );