You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
4.4 KiB
172 lines
4.4 KiB
#!/usr/bin/python |
|
# |
|
# Copyright (C) 2016 The Android Open Source Project |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
"""Reset a USB device (presumbly android phone) by serial number. |
|
|
|
Given a serial number, inspects connected USB devices and issues USB |
|
reset to the one that matches. Python version written by Than |
|
McIntosh, based on a perl version from Chris Ferris. Intended for use |
|
on linux. |
|
|
|
""" |
|
|
|
import fcntl |
|
import getopt |
|
import locale |
|
import os |
|
import re |
|
import shlex |
|
import subprocess |
|
import sys |
|
|
|
# Serial number of device that we want to reset |
|
flag_serial = None |
|
|
|
# Debugging verbosity level (0 -> no output) |
|
flag_debug = 0 |
|
|
|
USBDEVFS_RESET = ord("U") << (4*2) | 20 |
|
|
|
|
|
def verbose(level, msg): |
|
"""Print debug trace output of verbosity level is >= value in 'level'.""" |
|
if level <= flag_debug: |
|
sys.stderr.write(msg + "\n") |
|
|
|
|
|
def increment_verbosity(): |
|
"""Increment debug trace level by 1.""" |
|
global flag_debug |
|
flag_debug += 1 |
|
|
|
|
|
def issue_ioctl_to_device(device): |
|
"""Issue USB reset ioctl to device.""" |
|
|
|
try: |
|
fd = open(device, "wb") |
|
except IOError as e: |
|
error("unable to open device %s: " |
|
"%s" % (device, e.strerror)) |
|
verbose(1, "issuing USBDEVFS_RESET ioctl() to %s" % device) |
|
fcntl.ioctl(fd, USBDEVFS_RESET, 0) |
|
fd.close() |
|
|
|
|
|
# perform default locale setup if needed |
|
def set_default_lang_locale(): |
|
if "LANG" not in os.environ: |
|
warning("no env setting for LANG -- using default values") |
|
os.environ["LANG"] = "en_US.UTF-8" |
|
os.environ["LANGUAGE"] = "en_US:" |
|
|
|
|
|
def warning(msg): |
|
"""Issue a warning to stderr.""" |
|
sys.stderr.write("warning: " + msg + "\n") |
|
|
|
|
|
def error(msg): |
|
"""Issue an error to stderr, then exit.""" |
|
sys.stderr.write("error: " + msg + "\n") |
|
exit(1) |
|
|
|
|
|
# invoke command, returning array of lines read from it |
|
def docmdlines(cmd, nf=None): |
|
"""Run a command via subprocess, returning output as an array of lines.""" |
|
verbose(2, "+ docmdlines executing: %s" % cmd) |
|
args = shlex.split(cmd) |
|
mypipe = subprocess.Popen(args, stdout=subprocess.PIPE) |
|
encoding = locale.getdefaultlocale()[1] |
|
pout, perr = mypipe.communicate() |
|
if mypipe.returncode != 0: |
|
if perr: |
|
decoded_err = perr.decode(encoding) |
|
warning(decoded_err) |
|
if nf: |
|
return None |
|
error("command failed (rc=%d): cmd was %s" % (mypipe.returncode, args)) |
|
decoded = pout.decode(encoding) |
|
lines = decoded.strip().split("\n") |
|
return lines |
|
|
|
|
|
def perform(): |
|
"""Main driver routine.""" |
|
lines = docmdlines("usb-devices") |
|
dmatch = re.compile(r"^\s*T:\s*Bus\s*=\s*(\d+)\s+.*\s+Dev#=\s*(\d+).*$") |
|
smatch = re.compile(r"^\s*S:\s*SerialNumber=(.*)$") |
|
device = None |
|
found = False |
|
for line in lines: |
|
m = dmatch.match(line) |
|
if m: |
|
p1 = int(m.group(1)) |
|
p2 = int(m.group(2)) |
|
device = "/dev/bus/usb/%03d/%03d" % (p1, p2) |
|
verbose(1, "setting device: %s" % device) |
|
continue |
|
m = smatch.match(line) |
|
if m: |
|
ser = m.group(1) |
|
if ser == flag_serial: |
|
verbose(0, "matched serial %s to device " |
|
"%s, invoking reset" % (ser, device)) |
|
issue_ioctl_to_device(device) |
|
found = True |
|
break |
|
if not found: |
|
error("unable to locate device with serial number %s" % flag_serial) |
|
|
|
|
|
def usage(msgarg): |
|
"""Print usage and exit.""" |
|
if msgarg: |
|
sys.stderr.write("error: %s\n" % msgarg) |
|
print """\ |
|
usage: %s [options] XXYYZZ |
|
|
|
where XXYYZZ is the serial number of a connected Android device. |
|
|
|
options: |
|
-d increase debug msg verbosity level |
|
|
|
""" % os.path.basename(sys.argv[0]) |
|
sys.exit(1) |
|
|
|
|
|
def parse_args(): |
|
"""Command line argument parsing.""" |
|
global flag_serial |
|
|
|
try: |
|
optlist, args = getopt.getopt(sys.argv[1:], "d") |
|
except getopt.GetoptError as err: |
|
# unrecognized option |
|
usage(str(err)) |
|
if not args or len(args) != 1: |
|
usage("supply a single device serial number as argument") |
|
flag_serial = args[0] |
|
|
|
for opt, _ in optlist: |
|
if opt == "-d": |
|
increment_verbosity() |
|
|
|
|
|
set_default_lang_locale() |
|
parse_args() |
|
perform()
|
|
|