PCから物理的に切断/接続せずに、USBデバイスの接続をリセットすることは可能ですか?
具体的には、私のデバイスはデジタルカメラです。 gphoto2
を使用していますが、最近「デバイスの読み取りエラー」が発生するため、接続のソフトウェアリセットを実行したいと思います。
私の知る限り、カメラ用にカーネルモジュールがロードされていません。関連するように見える唯一のものは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"
Libfreenectがスリープ状態に戻すためのAPIを持たないため、これがKinectをリセットするために実際に使用するものです。私のGentooボックスにありますが、カーネルはsysfsに同じパス構造を使用するのに十分な新しいものでなければなりません。
明らかに1-4.6
ではありませんが、カーネルログ(dmesg
)からデバイスパスを取得するか、lsusb
のようなものを使用してベンダーIDと製品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
[ -e "$i" ] || continue
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がある場合にも役立ちます。
ここでの回答に基づいてプロセス全体を簡素化するPythonスクリプトを作成しました。
次のスクリプトをreset_usb.pyまたはclone this 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
脚本:
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, PIPE
import fcntl
instructions = '''
Usage: 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
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
'''
if len(sys.argv) < 2:
print(instructions)
sys.exit(0)
option = sys.argv[1].lower()
if 'help' in option:
print(instructions)
sys.exit(0)
def create_pci_list():
pci_usb_list = list()
try:
lspci_out = Popen('lspci -Dvmm', Shell=True, bufsize=64, stdin=PIPE, stdout=PIPE, close_fds=True).stdout.read().strip().decode('utf-8')
pci_devices = lspci_out.split('%s%s' % (os.linesep, os.linesep))
for pci_device in pci_devices:
device_dict = dict()
categories = pci_device.split(os.linesep)
for category in categories:
key, value = category.split('\t')
device_dict[key[:-1]] = value.strip()
if 'USB' not in device_dict['Class']:
continue
for root, dirs, files in os.walk('/sys/bus/pci/drivers/'):
slot = device_dict['Slot']
if slot in dirs:
device_dict['path'] = os.path.join(root, slot)
break
pci_usb_list.append(device_dict)
except Exception as ex:
print('Failed to list pci devices! Error: %s' % ex)
sys.exit(-1)
return pci_usb_list
def create_usb_list():
device_list = list()
try:
lsusb_out = Popen('lsusb -v', Shell=True, bufsize=64, stdin=PIPE, stdout=PIPE, close_fds=True).stdout.read().strip().decode('utf-8')
usb_devices = lsusb_out.split('%s%s' % (os.linesep, os.linesep))
for device_categories in usb_devices:
if not device_categories:
continue
categories = device_categories.split(os.linesep)
device_stuff = categories[0].strip().split()
bus = device_stuff[1]
device = device_stuff[3][:-1]
device_dict = {'bus': bus, 'device': device}
device_info = ' '.join(device_stuff[6:])
device_dict['description'] = device_info
for category in categories:
if not category:
continue
categoryinfo = category.strip().split()
if categoryinfo[0] == 'iManufacturer':
manufacturer_info = ' '.join(categoryinfo[2:])
device_dict['manufacturer'] = manufacturer_info
if categoryinfo[0] == 'iProduct':
device_info = ' '.join(categoryinfo[2:])
device_dict['device'] = device_info
path = '/dev/bus/usb/%s/%s' % (bus, device)
device_dict['path'] = path
device_list.append(device_dict)
except Exception as ex:
print('Failed to list usb devices! Error: %s' % ex)
sys.exit(-1)
return device_list
if 'listpci' in option:
pci_usb_list = create_pci_list()
for device in pci_usb_list:
print('path=%s' % device['path'])
print(' manufacturer=%s' % device['SVendor'])
print(' device=%s' % device['SDevice'])
print(' search string=%s %s' % (device['SVendor'], device['SDevice']))
sys.exit(0)
if 'list' in option:
usb_list = create_usb_list()
for device in usb_list:
print('path=%s' % device['path'])
print(' description=%s' % device['description'])
print(' manufacturer=%s' % device['manufacturer'])
print(' device=%s' % device['device'])
print(' search string=%s %s %s' % (device['description'], device['manufacturer'], device['device']))
sys.exit(0)
if len(sys.argv) < 3:
print(instructions)
sys.exit(0)
option2 = sys.argv[2]
print('Resetting device: %s' % option2)
# echo -n "0000:39:00.0" | tee /sys/bus/pci/drivers/xhci_hcd/unbind;echo -n "0000:39:00.0" | tee /sys/bus/pci/drivers/xhci_hcd/bind
def reset_pci_usb_device(dev_path):
folder, slot = os.path.split(dev_path)
try:
fp = open(os.path.join(folder, 'unbind'), 'wt')
fp.write(slot)
fp.close()
fp = open(os.path.join(folder, 'bind'), 'wt')
fp.write(slot)
fp.close()
print('Successfully reset %s' % dev_path)
sys.exit(0)
except Exception as ex:
print('Failed to reset device! Error: %s' % ex)
sys.exit(-1)
if 'pathpci' in option:
reset_pci_usb_device(option2)
if 'searchpci' in option:
pci_usb_list = create_pci_list()
for device in pci_usb_list:
text = '%s %s' % (device['SVendor'], device['SDevice'])
if option2 in text:
reset_pci_usb_device(device['path'])
print('Failed to find device!')
sys.exit(-1)
def reset_usb_device(dev_path):
USBDEVFS_RESET = 21780
try:
f = open(dev_path, 'w', os.O_WRONLY)
fcntl.ioctl(f, USBDEVFS_RESET, 0)
print('Successfully reset %s' % dev_path)
sys.exit(0)
except Exception as ex:
print('Failed to reset device! Error: %s' % ex)
sys.exit(-1)
if 'path' in option:
reset_usb_device(option2)
if 'search' in option:
usb_list = create_usb_list()
for device in usb_list:
text = '%s %s %s' % (device['description'], device['manufacturer'], device['device'])
if option2 in text:
reset_usb_device(device['path'])
print('Failed to find device!')
sys.exit(-1)
質問の特別な場合は、gphoto2とUSB上のカメラとの通信の問題であるため、gphoto2にはUSB接続をリセットするオプションがあります。
gphoto2 --reset
このオプションは、質問が行われた2010年には存在しなかったのかもしれません。
デバイス番号に基づいて特定のUSBデバイスをリセットするpythonスクリプトを作成しました。コマンド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()
リセットする最も簡単な方法は、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でデバイス名を見つける必要はありません。自動スクリプトに組み込むこともできます。
私はモジュールをリロードすることで一種の大ハンマーを使用しています。これは私の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
そして、これは私のdiplayマネージャーが起動した後にusb_reset.shを実行するsystemdサービスファイル/usr/lib/systemd/system/usbreset.serviceです:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
一致する製品/ベンダー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
特定のUSBデバイスをリセットするための単純なbashスクリプトを作成しました。
#!/bin/bash
#type lsusb to find "vendor" and "product" ID in terminal
set -euo pipefail
IFS=$'\n\t'
#edit the below two lines of vendor and product values using lsusb result
dev=$(lsusb -t | grep usbdevicename | grep 'If 1' | cut -d' ' -f13|cut -d"," -f1)
#VENDOR=05a3
#PRODUCT=9230
VENDOR=$(lsusb -s $dev | cut -d' ' -f6 | cut -d: -f1)
PRODUCT=$(lsusb -s $dev | cut -d' ' -f6 | cut -d: -f2)
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
VID(ベンダーID)とPID(製品ID)で識別される特定のデバイスでこの操作を実行したい場合があります。これは、この目的に役立つスクリプトで、気の利いたlibusbライブラリを使用しています。
ファーストラン:
Sudo apt-get install libusb-dev
次に、このc ++ファイルのresetDeviceConnectionが、vidおよびpidで識別されるデバイス接続をリセットするこのタスクを実行する必要があります。
#include <libusb-1.0/libusb.h>
int resetDeviceConnection(UINT_16 vid, UINT_16 pid){
/*Open libusb*/
int resetStatus = 0;
libusb_context * context;
libusb_init(&context);
libusb_device_handle * dev_handle = libusb_open_device_with_vid_pid(context,vid,pid);
if (dev_handle == NULL){
printf("usb resetting unsuccessful! No matching device found, or error encountered!\n");
resetStatus = 1;
}
else{
/*reset the device, if one was found*/
resetStatus = libusb_reset_device(dev_handle);
}
/*exit libusb*/
libusb_exit(context);
return resetStatus;
}
(私の個人的なTILカタログから盗まれた: https://github.com/Marviel/TIL/blob/master/unix_tools/Reset_specific_USB_Device.md )
誰かがハンマーを注文しましたか?これは、他のさまざまな回答からまとめられています。
#!/bin/bash
# Root required
if (( UID )); then
exec Sudo "$0" "$@"
fi
cd /sys/bus/pci/drivers
function reinit {(
local d="$1"
test -e "$d" || return
rmmod "$d"
cd "$d"
for i in $(ls | grep :); do
echo "$i" > unbind
done
sleep 1
for i in $(ls | grep :); do
echo "$i" > bind
done
modprobe "$d"
)}
for d in ?hci_???; do
echo " - $d"
reinit "$d"
done
これを試してみてください、それはソフトウェアの取り外し(イジェクト)です。
一部のデバイスでは、単にデバイスのバインドを解除できないことがあります。
例:
「Genius NetScroll 120」を削除または取り出したい。
その後、最初に接続されたUSBデバイスを確認します
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0020 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0020 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 03f0:231d Hewlett-Packard
Bus 001 Device 004: ID 138a:0007 Validity Sensors, Inc. VFS451 Fingerprint Reader
Bus 001 Device 005: ID 04f2:b163 Chicony Electronics Co., Ltd
Bus 002 Device 009: ID 0458:003a KYE Systems Corp. (Mouse Systems) NetScroll+ Mini Traveler / Genius NetScroll 120 **<----This my Mouse! XDDD**
OK、マウスを見つけました。バス002、デバイス009、idVendor 0458、idProduct 003aがあるため、これはマウスに関する参照デバイス情報です。
これは重要です。バス番号はデバイスへの開始名パスであり、製品IDとベンダーをチェックして、削除する正しいデバイスを確認します。
$ ls /sys/bus/usb/drivers/usb/
1-1/ 1-1.1/ 1-1.3/ 1-1.5/ 2-1/ 2-1.3/ bind uevent unbind usb1/ usb2/
フォルダーに注意を払い、フォルダー番号2で始まることを確認します。バスが002であるため、これを確認し、マウス情報に関する正しいidVendorおよびidProductを含む各フォルダーを1つずつ確認します。
この場合、次のコマンドで情報を取得します。
cat /sys/bus/usb/drivers/usb/2-1.3/idVendor
0458
cat /sys/bus/usb/drivers/usb/2-1.3/idProduct
003a
OK、パス/sys/bus/usb/drivers/usb/2-1.3/は私の情報マウスと一致します! XDDD。
デバイスを削除する時が来ました!
su -c "echo 1 > /sys/bus/usb/drivers/usb/2-1.3/remove"
USBデバイスを再度接続すると、再び機能します!
デバイス名がわかっている場合は、このpythonスクリプトが機能します。
#!/usr/bin/python
"""
USB Reset
Call as "usbreset.py <device_file_path>"
With device_file_path like "/dev/bus/usb/bus_number/device_number"
"""
import fcntl, sys, os
USBDEVFS_RESET = ord('U') << (4*2) | 20
def main():
fd = os.open(sys.argv[1], os.O_WRONLY)
if fd < 0: sys.exit(1)
fcntl.ioctl(fd, USBDEVFS_RESET, 0)
os.close(fd)
sys.exit(0)
# end main
if __== '__main__':
main()
おそらくこれはカメラでも機能します:
次に、私の側のUSB 3.0
(kernel.org)Linuxで飢えた3.4.42
HDDを復活させました。 dmesg
は、360秒後にコマンドがタイムアウトになり(申し訳ありませんが、接続されたネットワークではなく、ここにsyslogをコピーできません)、ドライブが完全にハングしたと言いました。デバイスにアクセスするプロセスはカーネルでブロックされ、殺せません。 NFS
ハング、ZFS
ハング、dd
ハング。
これを行った後、すべてが再び機能しました。 dmesg
は、見つかったUSB
デバイスについて1行だけで通知しました。
私は、以下が詳細に何をするのか本当に分かりません。しかし、うまくいきました。
次の出力例は、2.6.32-5-686
カーネルを使用したDebian Squeezeからのものであるため、2.6以上で動作すると思います。
$ ls -al /dev/sdb
brw-rw---T 1 root floppy 8, 16 Jun 3 20:24 /dev/sdb
$ ls -al /sys/dev/block/8:16/device/rescan
--w------- 1 root root 4096 Jun 6 01:46 /sys/dev/block/8:16/device/rescan
$ echo 1 > /sys/dev/block/8:16/device/rescan
これが機能しない場合、おそらく他の誰かが実際のリセットをデバイスに送信する方法を見つけることができます。