USB Reset On Wake From Sleep
Despite a few major short-comings of my Lenovo laptop I still really love it. I am currently traveling a lot so the 3K monitor on a small form-factor and rugged SSD make it a good choice. The one thing lacking is good network support. It has no Ethernet port perhaps because it is too thin; which, is not such a big deal; but I've had lots of trouble with the WiFi card. I replaced it a few times and the the hardware ready-state gets reset sometimes, not sure why.
I like to use a wired network connection whenever I can so I got a D-Link DUB-E100 Ethernet Adapter which is a little dongle that plugs into one of my two USB ports and it works fine, except when waking from sleep the device is not usable.
I found a C program on Ask Ubuntu which also contains a Python version by Peter that I embellished and added as a gist.
#!/usr/bin/env python
# usbreset -- send a USB port reset to a USB device
#
# http://askubuntu.com/questions/645#answer-661
#
# $ sudo usbreset.py DUB
# Looking for device: DUB
# Executing command: `lsusb | grep DUB`
# Subprocess: b'Bus 001 Device 006: ID 2001:1a02 D-Link Corp. DUB-E100 Fast...'
# Found device 001 on bus 006 for "D-Link Corp. DUB-E100 Fast Ethernet Adapt..."
from __future__ import print_function
import os
import re
import sys
import fcntl
import argparse
import subprocess
USBDEVFS_RESET = 21780
def main():
command_line = argparse.ArgumentParser(description='USB device reset')
command_line.add_argument(
'desc', metavar='DESC',
type=str, nargs='?', default='USB',
help='Device description')
options = command_line.parse_args(sys.argv[1:])
print('Looking for device:', options.desc)
lsusb_cmd = 'lsusb | grep {}'.format(options.desc)
print('Executing command: `{}`'.format(lsusb_cmd))
lsusb_out = subprocess.check_output(lsusb_cmd, shell=True)
print('Subprocess: ', lsusb_out.strip())
parts = re.search(r'Bus (?P<bus>\d+) Device (?P<dev>\d+): ID [:\d\w]+ (?P<desc>.*)$', str(lsusb_out))
bus = parts.group('bus')
dev = parts.group('dev')
desc = parts.group('desc').strip()
print('Found device {} on bus {} for "{}"'.format(bus, dev, desc))
f = open('/dev/bus/usb/{}/{}'.format(bus, dev), 'w', os.O_WRONLY)
fcntl.ioctl(f, USBDEVFS_RESET, 0)
if __name__ == '__main__':
main()
To execute it manually I give it a unique identifier among USB devices, so I
could have given the entire D-Link Corp. DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772]
description; but, DUB
should be enough:
usbreset.py DUB
Here’s the full output surrounded by various networking calls:
stav@venix:~$ ip route get 8.8.8.8
RTNETLINK answers: Network is unreachable
stav@venix:~$ ls /sys/class/net/
enx78542ee5c627 lo wlp1s0
stav@venix:~$ sudo ethtool enx78542ee5c627
Settings for enx78542ee5c627:
Advertised link modes: Not reported
Advertised pause frame use: No
Advertised auto-negotiation: No
Speed: 10Mb/s
Duplex: Half
Auto-negotiation: off
Link detected: no
stav@venix:~$ nmcli g
STATE CONNECTIVITY
disconnected none
stav@venix:~$ sudo ~/bin/usbreset.py DUB
Looking for device: DUB
Executing command: `lsusb | grep DUB`
Subprocess: Bus 002 Device 003: ID 2001:1a02 D-Link Corp. DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772]
Found device 002 on bus 003 for "D-Link Corp. DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772]"
stav@venix:~$ ip route get 8.8.8.8
8.8.8.8 via 192.168.1.254 dev enx78542ee5c627 src 192.168.1.72
stav@venix:~$ sudo ethtool enx78542ee5c627
Settings for enx78542ee5c627:
Advertised link modes: 10baseT/Half 10baseT/Full
100baseT/Half 100baseT/Full
Advertised pause frame use: Symmetric
Advertised auto-negotiation: Yes
Speed: 100Mb/s
Duplex: Full
Auto-negotiation: on
Link detected: yes
stav@venix:~$ nmcli g
STATE CONNECTIVITY
connected full
To get it to run on resume from suspend I compiled it into /opt
:
stav@venix:~$ python3 -m compileall bin/usbreset.py
Compiling 'bin/usbreset.py'...
stav@venix:~$ sudo mkdir /opt/stav
stav@venix:~$ sudo mv bin/__pycache__/usbreset.cpython-35.pyc /opt/stav
stav@venix:~$ ls -l /opt/stav/
total 4
-rw-rw-r-- 1 stav stav 1380 nov 27 19:54 usbreset.cpython-35.pyc
Then I added a systemd
script to call it with the DUB
argument.
/lib/systemd/system-sleep/usbreset
:
#!/bin/sh
# http://askubuntu.com/questions/226278/run-script-on-wakeup#answer-620328
case $1/$2 in
post/suspend)
/usr/bin/python3 /opt/stav/usbreset.cpython-35.pyc DUB
;;
esac
Works well.