如何从命令行重置 USB 设备?

cmc*_*nty 202 command-line usb

是否可以在不从 PC 物理断开/连接的情况下重置 USB 设备的连接?

具体来说,我的设备是数码相机。我正在使用gphoto2,但最近我收到“设备读取错误”,因此我想尝试对连接进行软件重置。

据我所知,没有为相机加载内核模块。唯一看起来相关的是usbhid.

Li *_* Lo 142

将以下内容另存为 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;
}
Run Code Online (Sandbox Code Playgroud)

在终端中运行以下命令:

  1. 编译程序:

    $ cc usbreset.c -o usbreset
    
    Run Code Online (Sandbox Code Playgroud)
  2. 获取要重置的 USB 设备的总线和设备 ID:

    $ lsusb  
    Bus 002 Device 003: ID 0fe9:9010 DVICO  
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使我们编译的程序可执行:

    $ chmod +x usbreset
    
    Run Code Online (Sandbox Code Playgroud)
  4. 以sudo权限执行程序;通过运行以下命令对找到的<Bus><Device>id进行必要的替换lsusb

    $ sudo ./usbreset /dev/bus/usb/002/003  
    
    Run Code Online (Sandbox Code Playgroud)

以上程序来源:http : //marc.info/?l=linux-usb&m=121459435621262&w=2

  • 至少在 Debian 10 和 Ubuntu 20.04 中不需要自编译 - /usr/bin/usbreset 是包 usbutils 的一部分。请注意,这个使用设备 ID 而不是总线/开发编号 - 在上述情况下只需调用“usbreset 0fe9:9010”。 (5认同)
  • 这适用于 ubuntu 13.10。设备 ID 可能会有所不同。为了获得鼠标,我将上面的代码包含在几个 shell 命令中`echo $(lsusb | grep Mouse) mouse=$( lsusb | grep Mouse | perl -nE "/\D+(\d+)\D+(\d+) .+/; 打印 qq(\$1/\$2)") sudo /path/to/c-program/usbreset /dev/bus/usb/$mouse` (3认同)
  • @水瓶座,我也得到同样的错误“ioctl错误:是一个目录”。解决了吗? (2认同)

sso*_*low 81

我以前没有遇到过您的具体情况,所以我不确定它是否足够,但我发现重置 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"
Run Code Online (Sandbox Code Playgroud)

这是我用来重置 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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你做得很好!也许您还应该提到在脚本中执行 `echo 1 &gt; /sys/bus/usb/devices/whatever/authorized` 以在设备被禁用后立即重新启用该设备。我在我的鼠标和 USB 键盘上都做了,最后我得到了一个完全失聪的系统:) (11认同)
  • 任何试图切换到`| 的人的注意事项 sudo tee ...` 特权`/sys` 方法写道:如果你还没有缓存你的 sudo 凭据,那会很糟糕。`sudo sh -c "..."` 在 sudo 需要提示输入密码时按预期工作。 (4认同)
  • `find /sys/bus/usb/devices/*/authorized -exec sh -c 'echo 0 &gt; ${0}; echo 1 &gt; ${0}' {} \;` 对我来说很有魅力。ty 用于指向授权文件。 (2认同)
  • @MarcH 不,这肯定不会重新启动 USB 设备。即使 USBDEVFS_RESET ioctl() 也没有这种能力。我还没有看到一些支持 USB 端口电源循环的硬件(主板)。但此外,这个 /sys/bus/usb/devices/*/authorized 东西似乎甚至没有使端口进入“重置请求”状态(数据+/-都低了几分之一秒)。但是,它确实使我的自定义 CDC ACM 设备显示出实际重置的所有迹象...这是否可能只是针对指示的 USB 端口在主机 PC 和从属设备上重新加载软件堆栈? (2认同)

小智 65

这将重置所有 USB1/2/3 连接端口[1]:

for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do
  [ -e "$i" ] || continue
  echo "${i##*/}" > "${i%/*}/unbind"
  echo "${i##*/}" > "${i%/*}/bind"
done
Run Code Online (Sandbox Code Playgroud)

我相信这会解决你的问题。如果您不想重置所有 USB 端点,您可以使用适当的设备 ID/sys/bus/pci/drivers/ehci_hcd


注意: [1]:*hci_hcd内核驱动程序通常控制 USB 端口。ohci_hcduhci_hcd用于 USB1.1 端口,ehci_hcd用于 USB2 端口和xhci_hcd用于 USB3 端口。(见https://en.wikipedia.org/wiki/Host_controller_interface_(USB,_Firewire)

  • 这是一个漂亮的解决方案。然而,在一些后来的内核和其他 *nix 发行版上,你会发现你需要用 `*hci-pci` 替换 `*hci_hcd`,因为 hci_hcd 驱动程序已经编译到内核中。 (5认同)
  • 尽管我收到了以下消息:`ls: cannot access /sys/bus/pci/drivers/ehci_hcd/: No such file or directory` 这解决了问题,鼠标立即开始工作。+1 (2认同)
  • @Otheus OHCI 和 UHCI 是 USB 1.1 主机标准,EHCI 是 USB 2.0 主机标准,XHCI 是 USB 3.0 主机标准。 (2认同)
  • 在香蕉派上,显然没有 PCI 总线,我不得不使用以下内容:`for i in /sys/bus/usb/drivers/*/*:*; 做` (2认同)

pan*_*.de 31

由于 APT 包 usbutils 提供了 usbreset 二进制文件,您只需通过以下方式重置 USB 设备:

usbreset ${USB_ID}

e.g.
usbreset 0d8c:0102
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案,而不是编译我自己的工具! (2认同)

mca*_*ans 13

我创建了一个 Python 脚本,它根据此处的答案简化了整个过程。

将下面的脚本保存为 reset_usb.py 或克隆这个 repo

用法:

python reset_usb.py help  # Show this help
sudo python reset_usb.py list  # List all USB devices
sudo python reset_usb.py path /dev/bus/usb/XXX/YYY  # Reset USB device using path /dev/bus/usb/XXX/YYY
sudo python reset_usb.py search "search terms"  # Search for USB device using the search terms within the search string returned by list and reset matching device
sudo python reset_usb.py listpci  # List all PCI USB devices
sudo python reset_usb.py pathpci /sys/bus/pci/drivers/.../XXXX:XX:XX.X  # Reset PCI USB device using path /sys/bus/pci/drivers/.../XXXX:XX:XX.X
sudo python reset_usb.py searchpci "search terms"  # Search for PCI USB device using the search terms within the search string returned by listpci and reset matching device
Run Code Online (Sandbox Code Playgroud)

脚本:

python reset_usb.py help  # Show this help
sudo python reset_usb.py list  # List all USB devices
sudo python reset_usb.py path /dev/bus/usb/XXX/YYY  # Reset USB device using path /dev/bus/usb/XXX/YYY
sudo python reset_usb.py search "search terms"  # Search for USB device using the search terms within the search string returned by list and reset matching device
sudo python reset_usb.py listpci  # List all PCI USB devices
sudo python reset_usb.py pathpci /sys/bus/pci/drivers/.../XXXX:XX:XX.X  # Reset PCI USB device using path /sys/bus/pci/drivers/.../XXXX:XX:XX.X
sudo python reset_usb.py searchpci "search terms"  # Search for PCI USB device using the search terms within the search string returned by listpci and reset matching device
Run Code Online (Sandbox Code Playgroud)

  • 这是这个问题的最佳答案。 (2认同)

Pet*_*ter 11

我需要在 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
Run Code Online (Sandbox Code Playgroud)

就我而言,它是 cp210x 驱动程序(我可以从中看出lsmod | grep usbserial),因此您可以将上述代码段保存为 reset_usb.py,然后执行以下操作:

sudo python reset_usb.py cp210x
Run Code Online (Sandbox Code Playgroud)

如果您的系统上还没有 ac 编译器设置,但您有 python,这也可能会有所帮助。


小智 6

最快的重置方法是重置 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
Run Code Online (Sandbox Code Playgroud)

这应该适用于大多数 PC 环境。但是,如果您使用的是某些自定义硬件,则可以简单地遍历设备名称。使用此方法,您无需通过 lsusb 找出设备名称。您也可以合并到自动化脚本中。


Ulr*_*ter 5

我通过重新加载模块来使用一种大锤。这是我的 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
Run Code Online (Sandbox Code Playgroud)

这是我的 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
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,在我的系统上,这些内核模块不是独立于内核的,所以这不起作用:`rmmod: ERROR: Module xhci_pci is builtin。` (5认同)

mvi*_*eck 5

由于问题的特殊情况是 gphoto2 与 USB 上的相机的通信问题,因此 gphoto2 中有一个选项可以重置其 USB 连接:

gphoto2 --reset
Run Code Online (Sandbox Code Playgroud)

也许这个选项在 2010 年被问到时并不存在。


小智 5

我制作了一个 python 脚本,它将根据设备编号重置特定的 USB 设备。您可以通过命令 lsusb 找到设备号。

例如:

$ lsusb

Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
Run Code Online (Sandbox Code Playgroud)

在此字符串中 004 是设备编号

$ lsusb

Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
Run Code Online (Sandbox Code Playgroud)


cmc*_*nty 5

这是只会重置匹配的产品/供应商 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
Run Code Online (Sandbox Code Playgroud)