Est-il possible de réinitialiser la connexion d'un périphérique USB sans se déconnecter/se connecter physiquement du PC?
Plus précisément, mon appareil est un appareil photo numérique. J'utilise gphoto2
, mais dernièrement, des "erreurs de lecture de périphérique" se produisent, j'aimerais donc essayer de réinitialiser le logiciel de la connexion.
D'après ce que je peux dire, aucun module de noyau n'est chargé pour la caméra. Le seul qui semble lié est usbhid
.
Enregistrez les éléments suivants sous 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;
}
Le exécuter les commandes suivantes dans le terminal:
Compiler le programme:
$ cc usbreset.c -o usbreset
Obtenez les identifiants de bus et de périphérique du périphérique USB que vous souhaitez réinitialiser:
$ lsusb
Bus 002 Device 003: ID 0fe9:9010 DVICO
Rendre notre programme compilé exécutable:
$ chmod +x usbreset
Exécutez le programme avec le privilège Sudo. effectuez la substitution nécessaire pour les ids <Bus>
et <Device>
tels qu'ils ont été trouvés en exécutant la commande lsusb
:
$ Sudo ./usbreset /dev/bus/usb/002/003
Source du programme ci-dessus: http://marc.info/?l=linux-usb&m=121459435621262&w=2
Je ne me suis jamais trouvé dans votre situation particulière auparavant, alors je ne sais pas si cela suffira, mais la façon la plus simple que j'ai trouvée de réinitialiser un périphérique USB est cette commande: (Aucune application externe nécessaire)
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"
C’est celui que j’utilise pour réinitialiser mon Kinect car libfreenect ne semble pas avoir d’API pour le remettre en veille. C'est sur ma boîte Gentoo, mais le noyau devrait être suffisamment nouveau pour utiliser la même structure de chemin pour sysfs.
Votre nom ne serait évidemment pas 1-4.6
mais vous pouvez extraire ce chemin du périphérique de votre journal du noyau (dmesg
) ou vous pouvez utiliser quelque chose comme lsusb
pour obtenir les ID fournisseur et produit, puis utiliser une commande rapide comme celle-ci pour répertorier le lien entre les chemins. à différentes paires ID fournisseur/produit:
for X in /sys/bus/usb/devices/*; do
echo "$X"
cat "$X/idVendor" 2>/dev/null
cat "$X/idProduct" 2>/dev/null
echo
done
Ceci réinitialisera tous les ports USB1/2/3 connectés [1]:
for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do
[ -e "$i" ] || continue
echo "${i##*/}" > "${i%/*}/unbind"
echo "${i##*/}" > "${i%/*}/bind"
done
Je crois que cela résoudra votre problème. Si vous ne souhaitez pas réinitialiser tous les points de terminaison USB, vous pouvez utiliser l'ID de périphérique approprié à partir de /sys/bus/pci/drivers/ehci_hcd
Remarques: [1]: les pilotes du noyau *hci_hcd
contrôlent généralement les ports USB. ohci_hcd
et uhci_hcd
correspondent aux ports USB 1.1, ehci_hcd
aux ports USB2 et xhci_hcd
aux ports USB3. (voir https://en.wikipedia.org/wiki/Host_controller_interface_ (USB, _Firewire) )
J'avais besoin d'automatiser cela dans un script python, alors j'ai adapté la réponse extrêmement utile de LiLo aux éléments suivants:
#!/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
Dans mon cas, il s’agissait du pilote cp210x (que je pourrais lire à partir de lsmod | grep usbserial
), de sorte que vous puissiez enregistrer le fragment de code ci-dessus sous le nom reset_usb.py, puis procédez comme suit:
Sudo python reset_usb.py cp210x
Cela pourrait également être utile si vous n'avez pas déjà configuré le compilateur c sur votre système, mais que vous avez le python.
J'ai créé un script Python qui simplifie l'ensemble du processus en fonction des réponses fournies.
Enregistrez le script ci-dessous sous le nom reset_usb.py ou clone this repo .
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 /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
Scénario:
#!/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)
J'utilise une sorte de sledgehammer en rechargeant les modules. Voici mon script 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
Et voici mon fichier de service systemd /usr/lib/systemd/system/usbreset.service qui exécute usb_reset.sh après le démarrage de mon gestionnaire de diplay:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
Comme le cas particulier de la question est un problème de communication de gphoto2 avec un appareil photo sur USB, il existe une option dans gphoto2 pour réinitialiser sa connexion USB:
gphoto2 --reset
Peut-être que cette option n'existait pas en 2010 lorsque la question a été posée.
J'ai créé un script python qui réinitialisera un périphérique USB particulier en fonction du numéro de périphérique. Vous pouvez trouver le numéro de périphérique à l'aide de la commande lsusb.
par exemple:
$ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
Dans cette chaîne 004 est le numéro de périphérique
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()
Le moyen le plus rapide de réinitialiser consiste à réinitialiser le contrôleur USB lui-même. Cela obligera udev à annuler l’enregistrement du périphérique lors de la déconnexion, et l’enregistrement est de retour une fois que vous l’activez.
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
Cela devrait fonctionner pour la plupart des environnements PC. Toutefois, si vous utilisez du matériel personnalisé, vous pouvez simplement parcourir les noms de périphériques. Avec cette méthode, vous n'avez pas besoin de connaître le nom du périphérique à l'aide de lsusb. Vous pouvez également intégrer un script automatisé.
Voici un script qui réinitialisera uniquement un ID de produit/fournisseur correspondant.
#!/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
Parfois, je souhaite effectuer cette opération sur un périphérique particulier, identifié par VID (identifiant du fournisseur) et PID (identifiant du produit). C'est un script que j'ai trouvé utile à cette fin, qui utilise la bibliothèque nifty libusb.
Première exécution:
Sudo apt-get install libusb-dev
Ensuite, resetDeviceConnection de ce fichier c ++ doit exécuter cette tâche, en réinitialisant une connexion de périphérique identifiée par vid et 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;
}
(volé de mon catalogue TIL personnel: https://github.com/Marviel/TIL/blob/master/unix_tools/Reset_specific_USB_Device.md )
Est-ce que quelqu'un a commandé un sledgehammer? Ceci est reconstitué à partir de diverses autres réponses ici.
#!/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
Essayez ceci, il s’agit d’un débranchement de logiciel (Eject).
Parfois, cela ne fonctionne pas simplement en déconnectant un périphérique pour certains périphériques.
Exemple:
Je souhaite retirer ou éjecter mon "Genius NetScroll 120".
Ensuite, je vérifie d'abord mon périphérique USB connecté
$ 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, j'ai trouvé ma souris, elle a les bus 002, 009, idVendor 0458 et idProduct 003a. Il s'agit donc d'une information de référence sur la souris.
Ceci est important, le numéro de bus est le chemin du nom de début du périphérique et je vérifierai l'ID du produit et le fournisseur pour s'assurer que le périphérique correct à supprimer.
$ 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/
Faites attention aux dossiers, vérifiez le début avec le dossier numéro 2, je vais vérifier celui-ci car mon bus est 002, et un par un, j'ai vérifié chaque dossier contenant le bon idVendor et idProduct à propos de mes informations de souris.
Dans ce cas, je vais récupérer les informations avec cette commande:
cat /sys/bus/usb/drivers/usb/2-1.3/idVendor
0458
cat /sys/bus/usb/drivers/usb/2-1.3/idProduct
003a
Ok, le chemin /sys/bus/usb/drivers/usb/2-1.3/ correspond à ma souris d’information! XDDD.
Il est temps de retirer l'appareil!
su -c "echo 1 > /sys/bus/usb/drivers/usb/2-1.3/remove"
Rebranchez le périphérique USB et ça marche encore!
j'ai fait un script bash simple pour réinitialiser un périphérique USB particulier.
#!/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
Si vous connaissez le nom de votre appareil, ce script python fonctionnera:
#!/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()
Peut-être que cela fonctionne aussi pour une caméra:
Après avoir relancé un disque dur USB 3.0
affamé sur un 3.4.42
(kernel.org) Linux de mon côté. dmesg
a dit que les commandes expiraient au bout de 360 secondes (désolé, je ne peux pas copier le journal système ici, pas de réseaux connectés) et que le lecteur est complètement bloqué. Les processus accédant à l'appareil étaient bloqués dans le noyau, impossible à tuer. NFS
Hung, ZFS
Name__ Hung, dd
Hung.
Après cela, tout a encore fonctionné. dmesg
a indiqué une seule ligne à propos du USB
trouvé.
Je n'ai vraiment aucune idée de ce qui suit fait en détail. Mais cela a fonctionné.
L'exemple de sortie suivant provient de Debian Squeeze avec le noyau 2.6.32-5-686
. Je pense donc que cela fonctionne pour les versions 2.6 et supérieures:
$ 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
Si cela ne fonctionne pas, quelqu'un d'autre peut peut-être comprendre comment envoyer une réinitialisation réelle à un périphérique.