Yuhang Zheng

从命令行重置USB设备

N 人看过

问题描述

是否可以重置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;
}

在终端中运行以下命令:

  1. 编译程序:

    $ cc usbreset.c -o usbreset
  2. 获取要重置的USB设备的总线和设备ID:

    $ lsusb  
    Bus 002 Device 003: ID 0fe9:9010 DVICO  
  3. 让我们的编译程序可执行:

    $ chmod +x usbreset
  4. 用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_hcduhci_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

参考资料