¿Es posible restablecer la conexión de un dispositivo USB, sin desconectar/conectar físicamente desde la PC?
Específicamente, mi dispositivo es una cámara digital. Estoy usando gphoto2
, pero últimamente recibo "errores de lectura del dispositivo", así que me gustaría intentar restablecer el software de la conexión.
Por lo que puedo decir, no se están cargando módulos del núcleo para la cámara. El único que parece relacionado es usbhid
.
Guarde lo siguiente como 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;
}
El ejecutar los siguientes comandos en la terminal:
Compila el programa:
$ cc usbreset.c -o usbreset
Obtenga la identificación de bus y dispositivo del dispositivo USB que desea restablecer:
$ lsusb
Bus 002 Device 003: ID 0fe9:9010 DVICO
Haga que nuestro programa compilado sea ejecutable:
$ chmod +x usbreset
Ejecute el programa con privilegio Sudo; realice la sustitución necesaria de <Bus>
y <Device>
ids como se encuentra al ejecutar el comando lsusb
:
$ Sudo ./usbreset /dev/bus/usb/002/003
Fuente del programa anterior: http://marc.info/?l=linux-usb&m=121459435621262&w=2
No me he encontrado en sus circunstancias específicas antes, por lo que no estoy seguro de si hará lo suficiente, pero la forma más simple que he encontrado para restablecer un dispositivo USB es este comando: (No se necesitan aplicaciones externas)
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"
Ese es el que uso para restablecer mi Kinect ya que libfreenect parece no tener API para volverlo a dormir. Está en mi cuadro Gentoo, pero el núcleo debería ser lo suficientemente nuevo como para usar la misma estructura de ruta para sysfs.
Obviamente, el suyo no sería 1-4.6
pero puede extraer esa ruta del dispositivo del registro del kernel (dmesg
) o puede usar algo como lsusb
para obtener las ID del proveedor y del producto y luego use un comando rápido como este para enumerar cómo se relacionan las rutas con los diferentes pares de proveedor/ID de producto:
for X in /sys/bus/usb/devices/*; do
echo "$X"
cat "$X/idVendor" 2>/dev/null
cat "$X/idProduct" 2>/dev/null
echo
done
Esto restablecerá todos los puertos USB1/2/3 conectados [1]:
for i in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*; do
[ -e "$i" ] || continue
echo "${i##*/}" > "${i%/*}/unbind"
echo "${i##*/}" > "${i%/*}/bind"
done
Creo que esto resolverá tu problema. Si no desea restablecer todos los puntos finales USB, puede usar la ID de dispositivo apropiada desde /sys/bus/pci/drivers/ehci_hcd
Notas: [1]: los controladores de kernel *hci_hcd
suelen controlar los puertos USB. ohci_hcd
y uhci_hcd
son para puertos USB1.1, ehci_hcd
es para puertos USB2 y xhci_hcd
es para puertos USB3. (ver https://en.wikipedia.org/wiki/Host_controller_interface_ (USB, _Firewire) )
Necesitaba automatizar esto en una secuencia de comandos python, así que adapté la respuesta extremadamente útil de LiLo a lo siguiente:
#!/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
En mi caso, era el controlador cp210x (que pude ver por lsmod | grep usbserial
), por lo que podría guardar el fragmento anterior como reset_usb.py y luego hacer esto:
Sudo python reset_usb.py cp210x
Esto también podría ser útil si aún no tiene una configuración del compilador de c en su sistema, pero sí tiene python.
Creé un script Python que simplifica todo el proceso basado en las respuestas aquí.
Guarde el script a continuación como reset_usb.py o clone este repositorio .
Uso:
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
Guió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)
Como el caso especial de la pregunta es un problema de comunicación de gphoto2 con una cámara en USB, hay una opción en gphoto2 para restablecer su conexión USB:
gphoto2 --reset
Quizás esta opción no existía en 2010 cuando se hizo la pregunta.
Hice una secuencia de comandos python que restablecerá un dispositivo USB en particular en función del número de dispositivo. Puede encontrar el número de dispositivo desde el comando lsusb.
por ejemplo:
$ lsusb
Bus 002 Device 004: ID 046d:c312 Logitech, Inc. DeLuxe 250 Keyboard
En esta cadena 004 es el número de dispositivo
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()
La forma más rápida de restablecer será restablecer el controlador USB en sí. Hacerlo obligará a udev a cancelar el registro del dispositivo en la desconexión, y el registro volverá una vez que lo habilite.
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
Esto debería funcionar para la mayoría de los entornos de PC. Sin embargo, si está utilizando un hardware personalizado, simplemente puede recorrer los nombres de los dispositivos. Con este método no necesita encontrar el nombre del dispositivo por lsusb. También puede incorporar en un script automatizado.
Estoy usando una especie de mazo al recargar los módulos. Este es mi 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
Y este es mi archivo de servicio systemd /usr/lib/systemd/system/usbreset.service que ejecuta usb_reset.sh después de que mi administrador de diplay ha comenzado:
[Unit]
Description=usbreset Service
After=gdm.service
Wants=gdm.service
[Service]
Type=oneshot
ExecStart=/path/to/usb_reset.sh
Aquí hay un script que solo restablecerá una identificación de producto/proveedor coincidente.
#!/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
hice un script bash simple para restablecer un dispositivo USB en particular.
#!/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 veces quiero realizar esta operación en un dispositivo en particular, según lo identificado por VID (identificación del proveedor) y PID (identificación del producto). Este es un script que he encontrado útil para este propósito, que utiliza la ingeniosa biblioteca libusb.
Primer intento:
Sudo apt-get install libusb-dev
Luego, resetDeviceConnection de este archivo c ++ debe realizar esta tarea, de restablecer una conexión de dispositivo como se identifica por vid y 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;
}
(robado de mi catálogo personal de TIL: https://github.com/Marviel/TIL/blob/master/unix_tools/Reset_specific_USB_Device.md )
¿Alguien ordenó un mazo? Esto se reconstruye a partir de varias otras respuestas aquí.
#!/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
Intente esto, es un desenchufe de software (Expulsar).
A veces no funciona simplemente desconectar el dispositivo para algunos dispositivos.
Ejemplo:
Quiero eliminar o expulsar mi "Genius NetScroll 120".
Entonces primero verifico mi dispositivo usb conectado
$ 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, encontré mi mouse, tiene un Bus 002, Dispositivo 009, idVendor 0458 e idProduct 003a, así que esta es una información del dispositivo de referencia sobre el mouse.
Esto es importante, el número de Bus es la ruta de inicio del nombre del dispositivo y comprobaré el Id. Del producto y el Proveedor para asegurarme de que se elimine el dispositivo correcto.
$ 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/
Preste atención a las carpetas, verifique el comienzo con la carpeta número 2, comprobaré esta porque mi Bus es 002, y una por una compruebo cada carpeta que contiene el idVendor y idProduct correctos sobre la información de mi mouse.
En este caso, recuperaré la información con este comando:
cat /sys/bus/usb/drivers/usb/2-1.3/idVendor
0458
cat /sys/bus/usb/drivers/usb/2-1.3/idProduct
003a
Ok, la ruta /sys/bus/usb/drivers/usb/2-1.3/ coincide con mi mouse de información. XDDD.
¡Es hora de quitar el dispositivo!
su -c "echo 1 > /sys/bus/usb/drivers/usb/2-1.3/remove"
¡Vuelva a enchufar el dispositivo usb y vuelve a funcionar!
Si conoce el nombre de su dispositivo, este python script funcionará:
#!/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()
Quizás esto también funcione para una cámara:
Después revivió un USB 3.0
HDD hambriento en un 3.4.42
(kernel.org) Linux de mi lado. dmesg
dijo, que estaba agotando los comandos después de 360 segundos (lo siento, no puedo copiar el registro del sistema aquí, no las redes conectadas) y la unidad se bloqueó por completo. Los procesos que acceden al dispositivo fueron bloqueados en el núcleo, imposibles de matar. NFS
colgado, ZFS
colgado, dd
colgado.
Después de hacer esto, todo volvió a funcionar. dmesg
contó una sola línea sobre el dispositivo USB
encontrado.
Realmente no tengo idea de lo que sigue en detalle. Pero funcionó.
El siguiente resultado de ejemplo es de Debian Squeeze con 2.6.32-5-686
kernel, así que creo que funciona para 2.6 y superior:
$ 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 esto no funciona, quizás alguien más pueda descubrir cómo enviar un reinicio real a un dispositivo.