问题描述
是否可以重置USB设备的连接,而无需从PC断开/连接?
具体来说,我的设备是数码相机。我正在使用gphoto2,但最近我收到“设备读取错误”,所以我想尝试执行连接的software-reset。
从我可以告诉的是,没有为相机加载内核模块。唯一与外表相关的是usbhid。
最佳解决办法
将以下内容保存为usbreset.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
int main(int argc, char **argv) { const char *filename; int fd; int rc;
if (argc != 2) { fprintf(stderr, "Usage: usbreset device-filename\n"); return 1; } filename = argv[1];
fd = open(filename, O_WRONLY); if (fd < 0) { perror("Error opening output file"); return 1; }
printf("Resetting USB device %s\n", filename); rc = ioctl(fd, USBDEVFS_RESET, 0); if (rc < 0) { perror("Error in ioctl"); return 1; } printf("Reset successful\n");
close(fd); return 0; }
|
在终端中运行以下命令:
编译程序:
1
| $ cc usbreset.c -o usbreset
|
获取要重置的USB设备的总线和设备ID:
1 2
| $ lsusb Bus 002 Device 003: ID 0fe9:9010 DVICO
|
让我们的编译程序可执行:
用sudo权限执行程序;通过运行lsusb命令找到<Bus>和<Device> id的必要替代:
1
| $ sudo ./usbreset /dev/bus/usb/002/003
|
上述计划的来源:http://marc.info/?l=linux-usb&m=121459435621262&w=2
次佳解决办法
我之前没有发现自己处于特定的情况,所以我不确定它是否足够,但是我发现重置USB设备的最简单方法是使用以下命令:(无需外部应用程序)
1 2
| sudo sh -c "echo 0 > /sys/bus/usb/devices/1-4.6/authorized" sudo sh -c "echo 1 > /sys/bus/usb/devices/1-4.6/authorized"
|
这是我用来重置我的Kinect的实际之一,因为libfreenect似乎没有将它重新启动的API。它位于我的Gentoo框中,但内核应该足够新,可以为sysfs使用相同的路径结构。
您明显不会是1-4.6,但您可以从内核日志(dmesg)中提取该设备路径,也可以使用类似lsusb的内容获取供应商和产品ID,然后使用类似这样的快速命令列出路径相关到不同的供应商/产品ID对:
1 2 3 4 5 6
| for X in /sys/bus/usb/devices/*; do echo "$X" cat "$X/idVendor" 2>/dev/null cat "$X/idProduct" 2>/dev/null echo done
|
第三种解决办法
这将重置所有USB1 /2/3连接端口[1]:
1 2 3 4
| for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do echo "${i##*/}" > "${i%/*}/unbind" echo "${i##*/}" > "${i%/*}/bind" done
|
我相信这会解决你的问题。如果您不想重置所有USB端点,则可以使用/sys/bus/pci/drivers/ehci_hcd中的适当设备ID
注:[1]:*hci_hcd内核驱动程序通常控制USB端口。 ohci_hcd和uhci_hcd用于USB1.1端口,ehci_hcd用于USB2端口,xhci_hcd用于USB3端口。 (见https://en.wikipedia.org/wiki/Host_controller_interface_(USB,_Firewire))
第四种办法
我需要在python脚本中自动执行此操作,所以我将LiLo的以下非常有用的答案改编为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import os import sys from subprocess import Popen, PIPE import fcntl driver = sys.argv[-1] print "resetting driver:", driver USBDEVFS_RESET= 21780
try: lsusb_out = Popen("lsusb | grep -i %s"%driver, shell=True, bufsize=64, stdin=PIPE, stdout=PIPE, close_fds=True).stdout.read().strip().split() bus = lsusb_out[1] device = lsusb_out[3][:-1] f = open("/dev/bus/usb/%s/%s"%(bus, device), 'w', os.O_WRONLY) fcntl.ioctl(f, USBDEVFS_RESET, 0) except Exception, msg: print "failed to reset device:", msg
|
在我的情况下,它是cp210x驱动程序(我可以从lsmod | grep usbserial中得知),因此您可以将上面的代码片段保存为reset_usb.py,然后执行以下操作:
1
| sudo python reset_usb.py cp210x
|
如果您的系统上尚未安装c编译器,但您确实有python,这也可能会有所帮助。
第五种办法
通过重新加载模块,我正在使用一种大锤。这是我的usb_reset.sh脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #!/bin/bash
# USB drivers rmmod xhci_pci rmmod ehci_pci
# uncomment if you have firewire #rmmod ohci_pci
modprobe xhci_pci modprobe ehci_pci
# uncomment if you have firewire #modprobe ohci_pci
|
这是我的systemd服务文件/usr/lib/systemd/system/usbreset.service,它在我的diplay管理器启动后运行usb_reset.sh:
1 2 3 4 5 6 7 8
| [Unit] Description=usbreset Service After=gdm.service Wants=gdm.service
[Service] Type=oneshot ExecStart=/path/to/usb_reset.sh
|
第六种办法
最快的重置方法是重置USB控制器本身。这样做会强制udev在断开连接时取消注册设备,并且一旦启用它就注册。
1 2 3 4
| echo -n "0000:00:1a.0" | tee /sys/bus/pci/drivers/ehci_hcd/unbind echo -n "0000:00:1d.0" | tee /sys/bus/pci/drivers/ehci_hcd/unbind echo -n "0000:00:1a.0" | tee /sys/bus/pci/drivers/ehci_hcd/bind echo -n "0000:00:1d.0" | tee /sys/bus/pci/drivers/ehci_hcd/bind
|
这应该适用于大多数PC环境。但是,如果您正在使用某些自定义硬件,则只需遍历设备名称即可。使用这种方法,您不需要通过lsusb找出设备名称。您也可以将其纳入自动化脚本中。
第七种办法
由于该问题的特殊情况是gphoto2与USB上的摄像头之间的通信问题,因此gphoto2中有一个选项可重置其USB连接:
也许这个选项在2010年并不存在,当问题被问到。
第八种办法
我制作了一个python脚本,它将根据设备编号重置特定的USB设备。您可以从命令lsusb中找到设备编号。
例如:
1 2 3
| $ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
|
在这个字符串004是设备号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import os import argparse import subprocess
path='/sys/bus/usb/devices/'
def runbash(cmd): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) out = p.stdout.read().strip() return out
def reset_device(dev_num): sub_dirs = [] for root, dirs, files in os.walk(path): for name in dirs: sub_dirs.append(os.path.join(root, name))
dev_found = 0 for sub_dir in sub_dirs: if True == os.path.isfile(sub_dir+'/devnum'): fd = open(sub_dir+'/devnum','r') line = fd.readline() if int(dev_num) == int(line): print ('Your device is at: '+sub_dir) dev_found = 1 break
fd.close()
if dev_found == 1: reset_file = sub_dir+'/authorized' runbash('echo 0 > '+reset_file) runbash('echo 1 > '+reset_file) print ('Device reset successful')
else: print ("No such device")
def main(): parser = argparse.ArgumentParser() parser.add_argument('-d', '--devnum', dest='devnum') args = parser.parse_args()
if args.devnum is None: print('Usage:usb_reset.py -d <device_number> \nThe device number can be obtained from lsusb command result') return
reset_device(args.devnum)
if __name__=='__main__': main()
|
第九种办法
这里是只会重置匹配产品/供应商ID的脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #!/bin/bash
set -euo pipefail IFS=$'\n\t'
VENDOR="045e" PRODUCT="0719"
for DIR in $(find /sys/bus/usb/devices/ -maxdepth 1 -type l); do if [[ -f $DIR/idVendor && -f $DIR/idProduct && $(cat $DIR/idVendor) == $VENDOR && $(cat $DIR/idProduct) == $PRODUCT ]]; then echo 0 > $DIR/authorized sleep 0.5 echo 1 > $DIR/authorized fi done
|
参考资料