从命令行重置USB设备
问题描述
是否可以重置USB设备的连接,而无需从PC断开/连接?
具体来说,我的设备是数码相机。我正在使用gphoto2
,但最近我收到“设备读取错误”,所以我想尝试执行连接的software-reset。
从我可以告诉的是,没有为相机加载内核模块。唯一与外表相关的是usbhid
。
最佳解决办法
将以下内容保存为usbreset.c
/* usbreset -- send a USB port reset to a USB device */
#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;
}
在终端中运行以下命令:
编译程序:
$ cc usbreset.c -o usbreset
获取要重置的USB设备的总线和设备ID:
$ lsusb Bus 002 Device 003: ID 0fe9:9010 DVICO
让我们的编译程序可执行:
$ chmod +x usbreset
用sudo权限执行程序;通过运行
lsusb
命令找到<Bus>
和<Device>
id的必要替代:$ sudo ./usbreset /dev/bus/usb/002/003
上述计划的来源:http://marc.info/?l=linux-usb&m=121459435621262&w=2
次佳解决办法
我之前没有发现自己处于特定的情况,所以我不确定它是否足够,但是我发现重置USB设备的最简单方法是使用以下命令:(无需外部应用程序)
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对:
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]:
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的以下非常有用的答案改编为:
#!/usr/bin/env python
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,然后执行以下操作:
sudo python reset_usb.py cp210x
如果您的系统上尚未安装c编译器,但您确实有python,这也可能会有所帮助。
第五种办法
通过重新加载模块,我正在使用一种大锤。这是我的usb_reset.sh脚本:
#!/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:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
第六种办法
最快的重置方法是重置USB控制器本身。这样做会强制udev在断开连接时取消注册设备,并且一旦启用它就注册。
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连接:
gphoto2 --reset
也许这个选项在2010年并不存在,当问题被问到。
第八种办法
我制作了一个python脚本,它将根据设备编号重置特定的USB设备。您可以从命令lsusb中找到设备编号。
例如:
$ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
在这个字符串004是设备号
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的脚本。
#!/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