USB支持热插拔。拔出插入之后如果文件句柄(mount和losetup)资源被释放,那么旧的设备节点会释放(sda),重新插入后会重新生成设备节点sda;而如果旧资源没被释放,那么会生成新的设备节点sdb, sdc。旧的设备节点和资源会被泄露。
usb会自恢复一些异常。但是如果USB固件出现故障,需要上下电才能恢复,linux驱动无法处理这种故障,除非对驱动进行大的整改。
USB卡如果固定插在开发板,不考虑热插拔的话,那么修改旧有的驱动,这种故障的修复变成可能。
我们考虑两种故障场景:
运行的时候USB固件跑飞了(挂死了),需要对USB固件上下电才能恢复(假设单板的EPLD固件支持对USB进行下点或者上电);
另外一种场景是USB电源线和数据线(四根线)受外界影响,出现闪断故障,导致设备断链(这样会导致旧的资源未释放,而生成新的设备)。
这两种故障场景都是标准的linux设备驱动无法处理的。
故障自恢复需要梳理初始化流程和读写流程。
先考虑第一种场景,就是读写流程
USB读写流程的关键函数是usb_stor_invoke_transport
该函数会封装qtd,内部包含CBW,DATA和CSW下发到USB控制器;触发USB控制器DMA传输。如果出现协议不可恢复故障(CRC, TIMEOUT和BAD PID),会进入Handle_Errors流程,Handle_Errors流程会复位HUB等操作,这个流程如果恢复不了,那就悲剧了。也就是说,上下电故障需要在这个流程里面添加。
usb_stor_invoke_transport
Handle_Errors:
=>result = usb_stor_port_reset(us);
=>result = rc_lock = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
=>result = usb_reset_composite_device(us->pusb_dev, us->pusb_intf);
=>ret = usb_reset_device(udev);
=>ep0_reinit(udev);
=>ret = hub_port_init(parent_hub, udev, port1, i);
=>retval = hub_port_reset(hub, port1, udev, delay);
=>status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_RESET);
=>status = hub_port_wait_reset(hub, port1, udev, delay);
=>usb_unlock_device(us->pusb_dev);
=>if (result < 0)
usb_stor_report_device_reset(us);//通知上层重传
us->transport_reset(us);