Este posibilă resetarea conexiunii unui dispozitiv USB, fără a deconecta/conecta fizic de la PC?
Mai exact, dispozitivul meu este o cameră digitală. Folosesc gphoto2
, dar în ultima vreme primesc "erori de citire a dispozitivului", așa că aș dori să încerc să fac o resetare software a conexiunii.
Din câte îmi dau seama, nu se încarcă niciun modul kernel pentru camera foto. Singurul care pare să aibă legătură este usbhid
.
Salvează următorul cod ca 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;
}
Apoi, execută următoarele comenzi în terminal:
$ cc usbreset.c -o usbreset
$ lsusb
BUS 002 device 003: ID 0fe9:9010 DVICO
$ chmod +x usbreset
<Bus>
și <device>
cu id-urile tale, care le-ai primit când ai executat comanda lsusb
:$ sudo ./usbreset /dev/bus/usb/002/003
Sursa programului de mai sus: http://marc.info/?l=linux-usb&m=121459435621262&w=2
Mai înainte nu am avut așa nevoie, dar acum, după ce am căutat un pic de informație, am găsit acest script, care resetează dispozitivul USB și nu necesită aplicații externe:
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"
Acesta este cel mai actual program care-l folsoesc pentru a reseta Kinect
din libfreenect
și nu are nici API pentru a pune-l înapoi în regimul de somn. Testez cu Gentoo Box
, dar kernel-ul ar trebui să fie destul de nou ca să folosească același drum pentru sysfs
.
Din câte înțeleg, versiunea ta nu e 1-4.6
. Dar, poți să folosești ceva de genul lsusb
ca să obții ID-urile de produs și apoi să le folsești într-o comandă rapidă de genul asta pentru a afișa ce căi se referă la furnizor/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
Acest script va reseta toate dispozitivele din porturile USB 1/2/3 *:
for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do
[ -e "$i" ] || continue
echo "${i##*/}" > "${i%/*}/unbind"
echo "${i##*/}" > "${i%/*}/bind"
done
Cred că acest program va rezolva problema ta. Dacă nu doriți să resetați toate USB-urile conectate, puteți folosi ID-ul dispozitivului pe care îl primiți cu aici /sys/bus/pci/drivere/ehci_hcd
Notă de subsol:
hci_hcd
sunt drivere pentru Kernel, care de obicei controlează porturile USB. ohci_hcd
și uhci_hcd
sunt pentru porturi USB 1.1, ehci_hcd
este pentru porturi USB versiunea a 2-a și xhci_hcd
este pentru porturi USB cu versiunea a 3-a. (puteți să citiți mai detaliat pe WikiPedia)
Aveam nevoie să automatizez acest proces, așa că am decis să scriu programul lui LiLo în Python:
#!/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
În cazul meu am folosit cp210x driver (pe care l-ați putea folosi așa lsmod | grep usbserial
). Salvați fișierul cu cod ca reset_usb.py
și apoi executați această comandă:
sudo python reset_usb.py cp210x
Codul de mai sus ar putea fi folositor, dar aveți nevoie de Python instalat în sistem.
Am creat un script Python care simplifică întregul proces, codul este bazat pe răspunsurile de aici.
Salvați script-ul de mai jos ca reset_usb.py sau clonați acest repozitoriu.
Îl folosiți așa:
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
Script-ul:
#!/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)
Cel mai rapid mod de a reseta conexiunea este să resetezi însăși controlerul USB. Făcând astfel, se pune în aplicare udev
pentru a retrage dispozitivul, iar dispozitivul este înapoi conectat după ce-l activați.
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
Acest program ar trebui să lucreze pentru un PC-mediu. Cu toate acestea, dacă utilizați un hardware personalizat, puteți pur și simplu repeta încercarea cu nume de dispozitiv. Cu această metodă nu aveți nevoie de numele dispozitivului de lsusb
. Puteți încorpora programa totul într-un script automat, care va funcționa la fel de bine.
Am făcut un script în Python care resetează un anumit dispozitiv USB, selectat după ID-ul dispozitivului. Puteți afla ID-ul dispozitivului executând comanda lsusb
.
Exemplu:
$ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
În acest șir 004 este ID-ul dispozitivului
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()
Am folosit un fel de baros pentru reîncărcarea modulelor.
Acest lucru este exemplul meu pentru 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
Și am creat și un service de sistem (systemd service
) drumul către fișier este: /usr/lib/systemd/system/usbreset.service
. Acest service de sistem ruleaza usb_reset.sh
după ce managerul pentru ecran s-a pornit:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
Acest script va reseta doar prima potrivire: product/vendor 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
Am făcut un script în Bash care resetează conexiunea dispozitivului cu portul USB:
#!/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
A comandat cineva un baros :) ? Răspunsul meu este inspirat din mai multe răspunsuri de pe această pagină.
#!/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
Câteodată am nevoie să fac această operație pe un dispozitiv nou, identificat cu VID (vendor id) și PID (id-ul produsului). Acesta este un script folositor pentru așa scopuri (folosește biblioteca libusb
).
Prima executare:
sudo apt-get install libusb-dev
Apoi, acest fișier C++ resetDeviceConnection efectuează această sarcină. Restabilește o conexiune bazată pe vid și 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;
}
Inspirat de pe pagina mea personală GitHub.
Dacă știi numele dispozitivului, poți să folosești acest script scris în 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 __name__ == '__main__':
main()
Încercați așa, următorul cod, este un program pentru a deconecta dispozitive (Eject).
Poate să nu funcționeze cu toate dispozitivele.
De exemplu:
Vreau să deconectez dispozitivul "Genius NetScroll 120".
Verificăm dacă este conectat dispozitivul:
$ 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, am gasit mouse-ul meu, este Bus 002, device 009, idVendor 0458 și idProduct 003a. Acum avem toate informațiile necesare despre mouse.
Acest lucru este important. Dacă nu introduceți corect aceste identificatore, programul nu va funcționa.
$ 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/
Fiți atenți la dosare, verificați incepand cu dosar numărul 2. Voi verifica asta pentru Bus 002, și treptat, unul câte unul verific fiecare dosar ca să conțină idVendor și idProduct corect.
Dacă greșiți id-urile această comandă nu va funcționa:
cat /sys/bus/usb/drivers/usb/2-1.3/idVendor
0458
cat /sys/bus/usb/drivers/usb/2-1.3/idProduct
003a
Ok, avem drumul /sys/bus/usb/drivere/usb/2-1.3/
și info despre mouse! XDDD.
Deconectăm dispozitivul!
su -c "echo 1 > /sys/bus/usb/drivers/usb/2-1.3/remove"
Conectați din nou dispozitivul USB și o să funcționeze din nou!
Posibil acest algoritm să funcționeze și pentru o cameră de fotografiat. Următorul exemplu este executat pe Debian Squeeze cu 2.6.32-5-686
kernel, dar cred că funcționează și pentru 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
Dacă acest program nu funcționează, încercați să deconectați fizic dispozitivuul.