Có thể thiết lập lại kết nối của thiết bị USB mà không cần ngắt kết nối/kết nối vật lý với PC không?
Cụ thể, thiết bị của tôi là một máy ảnh kỹ thuật số. Tôi đang sử dụng gphoto2
, nhưng gần đây tôi gặp "lỗi đọc thiết bị", vì vậy tôi muốn thử thiết lập lại phần mềm kết nối.
Từ những gì tôi có thể nói, không có mô-đun hạt nhân nào được tải cho máy ảnh. Người duy nhất có vẻ liên quan là usbhid
.
Lưu các mục sau dưới dạng 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;
}
Việc chạy các lệnh sau trong terminal:
Biên dịch chương trình:
$ cc usbreset.c -o usbreset
Nhận Bus và ID thiết bị của thiết bị USB bạn muốn đặt lại:
$ lsusb
Bus 002 Device 003: ID 0fe9:9010 DVICO
Làm cho chương trình biên dịch của chúng tôi thực thi:
$ chmod +x usbreset
Thực hiện chương trình với đặc quyền Sudo; thực hiện thay thế cần thiết cho các id <Bus>
và <Device>
được tìm thấy bằng cách chạy lệnh lsusb
:
$ Sudo ./usbreset /dev/bus/usb/002/003
Nguồn của chương trình trên: http://marc.info/?l=linux-usb&m=121459435621262&w=2
Trước đây tôi chưa thấy mình trong hoàn cảnh cụ thể của bạn, vì vậy tôi không chắc liệu nó có đủ không, nhưng cách đơn giản nhất tôi tìm thấy để đặt lại thiết bị USB là lệnh này: (Không cần ứng dụng bên ngoài)
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"
Đó là cái thực tế tôi sử dụng để đặt lại Kinect của mình vì libfreenect dường như không có API để đưa nó trở lại trạng thái ngủ. Nó nằm trong hộp Gentoo của tôi, nhưng kernel phải đủ mới để sử dụng cùng cấu trúc đường dẫn cho sysfs.
Bạn rõ ràng sẽ không phải là 1-4.6
nhưng bạn có thể kéo đường dẫn thiết bị đó từ nhật ký kernel của mình (dmesg
) hoặc bạn có thể sử dụng một cái gì đó như lsusb
để lấy ID nhà cung cấp và sản phẩm và sau đó sử dụng lệnh nhanh như thế này để liệt kê các đường dẫn liên quan như thế nào đến các cặp ID nhà cung cấp/sản phẩm khác nhau:
for X in /sys/bus/usb/devices/*; do
echo "$X"
cat "$X/idVendor" 2>/dev/null
cat "$X/idProduct" 2>/dev/null
echo
done
Điều này sẽ thiết lập lại tất cả các cổng USB1/2/3 đính kèm [1]:
for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do
[ -e "$i" ] || continue
echo "${i##*/}" > "${i%/*}/unbind"
echo "${i##*/}" > "${i%/*}/bind"
done
Tôi tin rằng điều này sẽ giải quyết vấn đề của bạn. Nếu bạn không muốn đặt lại tất cả các điểm cuối USB, bạn có thể sử dụng ID thiết bị phù hợp từ /sys/bus/pci/drivers/ehci_hcd
Lưu ý: [1]: trình điều khiển hạt nhân *hci_hcd
thường điều khiển các cổng USB. ohci_hcd
và uhci_hcd
dành cho cổng USB1.1, ehci_hcd
dành cho cổng USB2 và xhci_hcd
dành cho cổng USB3. (xem https://en.wikipedia.org/wiki/host_controll_interface_ (USB, _Firewire) )
Tôi cần tự động hóa điều này trong tập lệnh python, vì vậy tôi đã điều chỉnh câu trả lời cực kỳ hữu ích của LiLo sau đây:
#!/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
Trong trường hợp của tôi, đó là trình điều khiển cp210x (mà tôi có thể biết từ lsmod | grep usbserial
), vì vậy bạn có thể lưu đoạn mã trên dưới dạng reset_usb.py và sau đó thực hiện điều này:
Sudo python reset_usb.py cp210x
Điều này cũng có thể hữu ích nếu bạn chưa có thiết lập trình biên dịch c trên hệ thống của mình, nhưng bạn có python.
Tôi đã tạo một tập lệnh Python đơn giản hóa toàn bộ quá trình dựa trên các câu trả lời ở đây.
Lưu tập lệnh bên dưới dưới dạng reset_usb.py hoặc clone repo này .
Sử dụng:
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
Kịch bản:
#!/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)
Tôi đang sử dụng loại búa tạ bằng cách tải lại các mô-đun. Đây là tập lệnh usb_reset.sh của tôi:
#!/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
Và đây là tệp dịch vụ systemd của tôi /usr/lib/systemd/system/usbreset.service chạy usb_reset.sh sau khi trình quản lý bằng tốt nghiệp của tôi bắt đầu:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
Vì trường hợp đặc biệt của câu hỏi là sự cố giao tiếp của gphoto2 với camera trên USB, có một tùy chọn trong gphoto2 để đặt lại kết nối USB của nó:
gphoto2 --reset
Có lẽ tùy chọn này đã không tồn tại vào năm 2010 khi câu hỏi được hỏi.
Tôi đã tạo một tập lệnh python sẽ thiết lập lại một thiết bị USB cụ thể dựa trên số thiết bị. Bạn có thể tìm ra số thiết bị từ lệnh lsusb.
ví dụ:
$ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
Trong chuỗi này 004 là số thiết bị
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()
Cách nhanh nhất để thiết lập lại sẽ là tự thiết lập lại bộ điều khiển USB. Làm như vậy sẽ buộc udev hủy đăng ký thiết bị khi ngắt kết nối và đăng ký trở lại sau khi bạn bật thiết bị.
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
Điều này sẽ làm việc cho hầu hết các môi trường PC. Tuy nhiên, nếu bạn đang sử dụng một số phần cứng tùy chỉnh, bạn có thể chỉ cần lặp qua tên thiết bị. Với phương pháp này, bạn không cần phải tìm ra tên thiết bị theo lsusb. Bạn có thể kết hợp trong một kịch bản tự động là tốt.
Đây là tập lệnh sẽ chỉ đặt lại ID sản phẩm/nhà cung cấp phù hợp.
#!/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
Đôi khi tôi muốn thực hiện thao tác này trên một thiết bị cụ thể, như được xác định bởi VID (id nhà cung cấp) và PID (id sản phẩm). Đây là một kịch bản tôi thấy hữu ích cho mục đích này, sử dụng thư viện libusb tiện lợi.
Lần chạy đầu tiên:
Sudo apt-get install libusb-dev
Sau đó, resetDeviceConnection của tệp c ++ này sẽ thực hiện tác vụ này, đặt lại kết nối thiết bị như được xác định bởi vid và 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;
}
(bị đánh cắp từ danh mục TIL cá nhân của tôi: https://github.com/Marviel/TIL/blob/master/unix_tools/Reset_specific_USB_Device.md )
Có ai đó đặt mua một chiếc búa tạ? Điều này được chắp ghép từ nhiều câu trả lời khác nhau ở đây.
#!/bin/bash
# Root required
if (( UID )); then
exec Sudo "$0" "[email protected]"
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
Hãy thử điều này, đó là một phần mềm rút phích cắm (Eject).
Đôi khi không hoạt động đơn giản là hủy kết nối thiết bị cho một số thiết bị.
Thí dụ:
Tôi muốn xóa hoặc đẩy "Genius NetScroll 120" của mình.
Sau đó, tôi đầu tiên Kiểm tra thiết bị usb đính kèm của tôi
$ 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, tôi đã tìm thấy con chuột của mình, nó có Bus 002, Thiết bị 009, idVendor 0458 và idSản phẩm 003a, vì vậy đây là thông tin thiết bị tham khảo về chuột.
Điều này rất quan trọng, số Bus là đường dẫn tên bắt đầu đến thiết bị và tôi sẽ kiểm tra Id sản phẩm và Nhà cung cấp để đảm bảo xóa đúng thiết bị.
$ 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/
Chú ý vào các thư mục, kiểm tra bắt đầu với thư mục số 2, tôi sẽ kiểm tra cái này vì Bus của tôi là 002 và từng cái một tôi đã kiểm tra từng thư mục chứa idVendor chính xác và idSản phẩm về thông tin chuột của tôi.
Trong trường hợp này, tôi sẽ lấy thông tin bằng lệnh này:
cat /sys/bus/usb/drivers/usb/2-1.3/idVendor
0458
cat /sys/bus/usb/drivers/usb/2-1.3/idProduct
003a
Ok, đường dẫn /sys/bus/usb/drivers/usb/2-1.3/ khớp với chuột thông tin của tôi! XDDĐ.
Đã đến lúc tháo thiết bị!
su -c "echo 1 > /sys/bus/usb/drivers/usb/2-1.3/remove"
Cắm lại thiết bị usb và nó hoạt động trở lại!
tôi đã tạo một tập lệnh bash đơn giản để thiết lập lại thiết bị USB cụ thể.
#!/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
Nếu bạn biết tên thiết bị của mình, tập lệnh python này sẽ hoạt động:
#!/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()
Có lẽ điều này cũng làm việc cho một máy ảnh:
Theo dõi đã phục hồi một ổ cứng USB 3.0
bị bỏ đói trên một 3.4.42
(kernel.org) Linux về phía tôi. dmesg
nói, đó là thời gian ra lệnh sau 360 giây (xin lỗi, tôi không thể sao chép syslog ở đây, không kết nối mạng) và ổ đĩa bị treo hoàn toàn. Các quy trình truy cập thiết bị đã bị chặn trong kernel, không thể thực hiện được. NFS
hung, ZFS
hung, dd
hung.
Sau khi làm điều này, mọi thứ hoạt động trở lại. dmesg
chỉ nói một dòng về thiết bị USB
được tìm thấy.
Tôi thực sự không có ý tưởng gì sau đây làm chi tiết. Nhưng nó đã làm việc.
Kết quả ví dụ sau đây là từ Debian Squeeze với 2.6.32-5-686
kernel, vì vậy tôi nghĩ rằng nó hoạt động cho 2.6 trở lên:
$ 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
Nếu điều này không hoạt động, có lẽ ai đó khác có thể tìm ra cách gửi thiết lập lại thực sự cho thiết bị.