Merge "dialcheck: Make alarm timeout longer than the ssdp response timeout."
diff --git a/cmds/Makefile b/cmds/Makefile
index 45580b2..4bcca65 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -3,6 +3,13 @@
 PREFIX=/
 BINDIR=$(DESTDIR)$(PREFIX)/bin
 LIBDIR=$(DESTDIR)$(PREFIX)/lib
+GPYLINT=$(shell \
+    if which gpylint >/dev/null; then \
+      echo gpylint; \
+    else \
+      echo 'echo "(gpylint-missing)" >&2'; \
+    fi \
+)
 
 PORTABLE_TARGETS=\
 	balloon \
@@ -291,9 +298,15 @@
 		./$$d; \
 	done
 
-test: all $(TESTS)
+lint: $(filter-out options.py,$(wildcard *.py))
+	$(GPYLINT) $^
+
+test_only: all
 	./wvtest/wvtestrun $(MAKE) runtests
 
+test: all $(TESTS)
+	$(MAKE) test_only lint
+
 clean:
 	rm -f *.o $(TARGETS) \
 		$(HOST_TARGETS) \
diff --git a/cmds/bsa2bluez.py b/cmds/bsa2bluez.py
index 159c1b4..d3db9cf 100755
--- a/cmds/bsa2bluez.py
+++ b/cmds/bsa2bluez.py
@@ -1,8 +1,11 @@
 #!/usr/bin/python
+"""bsa2bluez: Automatic device import from Broadcom BSA to BlueZ."""
 
 import os
+import sys
 import xml.etree.ElementTree as ET
 
+
 def create_device_info_file(filename, link_key):
   with open(filename, 'w') as f:
     f.write('\n')
@@ -16,6 +19,7 @@
     f.write('SupportedTechnologies=BR/EDR;\n')
     f.write('Trusted=true\n')
     f.write('Blocked=false\n')
+    # pylint: disable=line-too-long
     f.write('Services=00001000-0000-1000-8000-00805f9b34fb;00001124-0000-1000-8000-00805f9b34fb;00001200-0000-1000-8000-00805f9b34fb;\n')
     f.write('Class=0x00050c\n')
     f.write('\n')
@@ -25,6 +29,7 @@
     f.write('Product=8192\n')
     f.write('Version=283\n')
 
+
 def create_device_cache_file(filename):
   with open(filename, 'w') as f:
     f.write('\n')
@@ -32,47 +37,45 @@
     f.write('Name=GFRM100\n')
     f.write('\n')
     f.write('[ServiceRecords]\n')
+    # pylint: disable=line-too-long
     f.write('0x00000000=35920900000A000000000900013503191000090004350D350619010009000135031900010900053503191002090006350909656E09006A09010009000935083506190100090100090100252D42726F6164636F6D20426C7565746F6F746820576972656C6573732052656D6F74652053445020536572766572090101250E52656D6F746520436F6E74726F6C0902003503090100\n')
     f.write('0x00010000=3601B60900000A000100000900013503191124090004350D350619010009001135031900110900053503191002090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002517476F6F676C6554562052656D6F746520436F6E74726F6C09010125214D756C74692D66756E6374696F6E2072656D6F74652077697468206B65797061640901022506476F6F676C6509020009017109020109011109020208400902030821090204280109020528010902063600BB3600B808222600B305010906A10185417508950126FF00050719002AFF008100C0050C0901A101854019002AFF0375109501150026FF038100C005010980A10185121981299315812593750895018140C0050C0901A1018513092015002564750895018142C08521092175089501150026FF008102852205010922A102093B950175101500264F01810206F0FF09227510964F01150026FF00820102C085F2090275089501150026FF00910285F3090375089510150026FF0081020902073508350609040909010009020B09010009020C090C8009020D280009020E280109020F090318090210090000\n')
     f.write('0x00010001=35520900000A000100010900013503191200090004350D350619010009000135031900010900093508350619120009010009020009010009020109005809020209200009020309011B0902042801090205090001\n')
 
-def main():
-  BT_MAC_FILE='/tmp/btmacaddress'
-  BSA_DEVICES_FILE='/user/bsa/bt_devices.xml'
-  BLUEZ_STORAGE_DIR='/user/bluez/lib/bluetooth'
 
+BT_MAC_FILE = '/tmp/btmacaddress'
+BSA_DEVICES_FILE = '/user/bsa/bt_devices.xml'
+BLUEZ_STORAGE_DIR = '/user/bluez/lib/bluetooth'
+
+
+def main():
   if not os.path.exists(BT_MAC_FILE):
-    print 'error: %s does not exist' % BT_MAC_FILE
-    return
+    sys.exit('error: %s does not exist' % BT_MAC_FILE)
 
   with open(BT_MAC_FILE, 'r') as f:
     bt_mac = f.read().upper().rstrip('\n')
 
   if len(bt_mac) != 17:
-    print 'error: bt_mac %s is invalid' % bt_mac
-    return
-
-  if not os.path.exists(BSA_DEVICES_FILE):
-    print 'error: %s does not exist' % BSA_DEVICES_FILE
-    return
+    sys.exit('error: bt_mac %s is invalid' % bt_mac)
 
   try:
     bsa_devices = ET.parse(BSA_DEVICES_FILE)
-  except:
-    print 'error: %s cannot be parsed' % BSA_DEVICES_FILE
+  except ET.ParseError as e:
+    print >>sys.stderr, 'error: failed to parse %r with exception %r' % (
+        BSA_DEVICES_FILE, e)
     with open(BSA_DEVICES_FILE, 'r') as f:
-      buffer = f.read()
-    print buffer[0:4096]
-    return
+      buf = f.read()
+    print >>sys.stderr, buf[0:4096]
+    sys.exit(1)
 
   adapter_dir = BLUEZ_STORAGE_DIR + '/' + bt_mac
   if not os.path.isdir(adapter_dir):
-    print 'create: BlueZ adapter dir %s' % adapter_dir
+    print >>sys.stderr, 'create: BlueZ adapter dir %s' % adapter_dir
     os.makedirs(adapter_dir)
 
   device_cache_dir = adapter_dir + '/cache'
   if not os.path.isdir(device_cache_dir):
-    print 'create: BlueZ device cache dir %s' % device_cache_dir
+    print >>sys.stderr, 'create: BlueZ device cache dir %s' % device_cache_dir
     os.makedirs(device_cache_dir)
 
   for parent in bsa_devices.getiterator():
@@ -90,36 +93,40 @@
 
     bd_addr = bd_addr.upper()
     if len(bd_addr) != 17:
-      print 'BSA device: GFRM100 has invalid bd_addr %s' % bd_addr
+      print >>sys.stderr, 'BSA device: GFRM100 has invalid bd_addr %s' % bd_addr
       continue
 
     link_key = link_key.upper().replace(':', '')
     if len(link_key) != 32:
-      print 'BSA device: GFRM100 has invalid link_key %s' % link_key
+      print >>sys.stderr, ('BSA device: GFRM100 has invalid link_key %s'
+                           % link_key)
       continue
 
-    print 'BSA device: GFRM100 at bd_addr %s link_key %s' % (bd_addr, link_key)
+    print >>sys.stderr, ('BSA device: GFRM100 at bd_addr %s link_key %s'
+                         % (bd_addr, link_key))
 
     device_dir = adapter_dir + '/' + bd_addr
     if not os.path.isdir(device_dir):
-      print 'create: BlueZ device dir %s' % device_dir
+      print >>sys.stderr, 'create: BlueZ device dir %s' % device_dir
       os.makedirs(device_dir)
     else:
-      print 'exists: BlueZ device dir %s' % device_dir
+      print >>sys.stderr, 'exists: BlueZ device dir %s' % device_dir
 
     device_info_file = device_dir + '/info'
     if not os.path.exists(device_info_file):
-      print 'create: BlueZ device info file %s' % device_info_file
+      print >>sys.stderr, 'create: BlueZ device info file %s' % device_info_file
       create_device_info_file(device_info_file, link_key)
     else:
-      print 'exists: BlueZ device info file %s' % device_info_file
+      print >>sys.stderr, 'exists: BlueZ device info file %s' % device_info_file
 
     device_cache_file = device_cache_dir + '/' + bd_addr
     if not os.path.exists(device_cache_file):
-      print 'create: BlueZ device cache file %s' % device_cache_file
+      print >>sys.stderr, ('create: BlueZ device cache file %s'
+                           % device_cache_file)
       create_device_cache_file(device_cache_file)
     else:
-      print 'exists: BlueZ device cache file %s' % device_cache_file
+      print >>sys.stderr, ('exists: BlueZ device cache file %s'
+                           % device_cache_file)
 
 if __name__ == '__main__':
   main()
diff --git a/cmds/burnin-flash.py b/cmds/burnin-flash.py
index d00c719..80dbfcd 100755
--- a/cmds/burnin-flash.py
+++ b/cmds/burnin-flash.py
@@ -5,8 +5,6 @@
 
 __author__ = 'dgentry@google.com (Denton Gentry)'
 
-import ctypes
-import fcntl
 import os
 import os.path
 import subprocess
@@ -17,7 +15,7 @@
 
 
 try:
-  import monotime  # pylint: disable-msg=unused-import,g-import-not-at-top
+  import monotime  # pylint: disable=unused-import,g-import-not-at-top
 except ImportError:
   pass
 try:
@@ -26,7 +24,6 @@
   gettime = time.time
 
 
-
 optspec = """
 burnin-flash [options...] /path/to/write/to
 --
@@ -38,7 +35,7 @@
 
 def main():
   o = options.Options(optspec)
-  (opt, flags, extra) = o.parse(sys.argv[1:])  #pylint: disable-msg=W0612
+  (opt, flags, extra) = o.parse(sys.argv[1:])  # pylint: disable=W0612
   if not opt.mtd:
     o.fatal('an mtd argument is required')
   if len(extra) != 1:
@@ -95,7 +92,7 @@
 
     ecc = py_mtd.eccstats(opt.mtd)
     # Ignore ecc.corrected, they are uncommon but normal.
-    print('%s: Corrected ECC error = %d' % (opt.mtd, ecc.corrected))
+    print '%s: Corrected ECC error = %d' % (opt.mtd, ecc.corrected)
     if ecc.failed != start_ecc.failed:
       print('%s: Uncorrectable ECC error during test, %d != %d' %
             (opt.mtd, ecc.failed, start_ecc.failed))
diff --git a/cmds/dialcheck-test-server.py b/cmds/dialcheck-test-server.py
index 2637384..6f360a1 100644
--- a/cmds/dialcheck-test-server.py
+++ b/cmds/dialcheck-test-server.py
@@ -1,14 +1,11 @@
 #!/usr/bin/python
-"""Fake SSDP server for unit tests.
-
-"""
+"""Fake SSDP server for unit tests."""
 
 import errno
 import os
 import signal
 import socket
 import SocketServer
-import struct
 import sys
 
 
@@ -21,6 +18,7 @@
 
 
 class SSDPHandler(SocketServer.BaseRequestHandler):
+
   def handle(self):
     self.request[1].sendto(notify, self.client_address)
 
@@ -49,10 +47,11 @@
   SocketServer.UDPServer.allow_reuse_address = True
   s = SocketServer.UDPServer(('', 0), SSDPHandler)
   s.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
-      socket.inet_aton('239.255.255.250') + socket.inet_aton('0.0.0.0'))
+                      socket.inet_pton(socket.AF_INET, '239.255.255.250') +
+                      socket.inet_pton(socket.AF_INET, '0.0.0.0'))
   sn = s.socket.getsockname()
   port = sn[1]
-  open(sys.argv[1], "w").write(str(port))
+  open(sys.argv[1], 'w').write(str(port))
   s.handle_request()
 
 
diff --git a/cmds/readubootver.py b/cmds/readubootver.py
index 76651be..28cc3cf 100755
--- a/cmds/readubootver.py
+++ b/cmds/readubootver.py
@@ -1,4 +1,5 @@
 #!/usr/bin/python
+"""readubootver: read U-Boot version."""
 
 import struct
 import sys
@@ -19,6 +20,7 @@
 
 
 def GetRootPartition():
+  """Identify the root partition from the kernel command line."""
   cmdline = ReadFile(CMDLINE_FILE)
   for opt in cmdline.split():
     if opt.startswith('root='):
@@ -27,12 +29,13 @@
         return 'kernel0'
       if rootfs == 'rootfs1':
         return 'kernel1'
-      print 'Unknown rootfs=' + rootfs0
+      print 'Unknown rootfs=' + rootfs
       sys.exit(1)
   return None
 
 
 def GetBootMtds():
+  """Get /dev file names for bootable MTDs."""
   all_mtds = ReadFile(MTD_FILE)
   boot_mtds = dict()
   for line in all_mtds.split('\n'):
@@ -46,6 +49,7 @@
 
 
 def ReadUbootHeader(device):
+  """Read U-Boot header from a /dev/mtd* device file."""
   try:
     with open(device, 'rb') as f:
       chunk = f.read(7*4 + 4 + 32)
@@ -86,8 +90,8 @@
     else:
       print 'ALTERNATE'
     print 'mtd: %s' % mtd
-    print 'size: %d' % header["size"]
-    print 'name: %s' % header["name"]
+    print 'size: %d' % header['size']
+    print 'name: %s' % header['name']
     print ''
 
 if __name__ == '__main__':
diff --git a/cmds/readubootver_test.py b/cmds/readubootver_test.py
index 5b13be1..e810049 100755
--- a/cmds/readubootver_test.py
+++ b/cmds/readubootver_test.py
@@ -8,12 +8,13 @@
 
 import readubootver
 
+
 class ReadUbootVerTest(unittest.TestCase):
 
   def setUp(self):
     self.tmp_dir = tempfile.mkdtemp()
-    self.save_MTD_FILE = readubootver.MTD_FILE
-    self.save_CMDLINE_FILE = readubootver.CMDLINE_FILE
+    self.save_mtd_file = readubootver.MTD_FILE
+    self.save_cmdline_file = readubootver.CMDLINE_FILE
     readubootver.MTD_FILE = tempfile.mktemp(prefix=self.tmp_dir)
     readubootver.CMDLINE_FILE = tempfile.mktemp(prefix=self.tmp_dir)
 
@@ -24,14 +25,14 @@
       shutil.rmtree(self.tmp_dir)
     except OSError:
       pass
-    readubootver.MTD_FILE = self.save_MTD_FILE
-    readubootver.CMDLINE_FILE = self.save_CMDLINE_FILE
+    readubootver.MTD_FILE = self.save_mtd_file
+    readubootver.CMDLINE_FILE = self.save_cmdline_file
 
   def testGetRootPartition(self):
     with open(readubootver.CMDLINE_FILE, 'w') as f:
-      f.write('ttyS0,115200 mtdparts=spi_flash:768k(loader),256k(env),' +
-              '128k(var1),128k(var2),128k(sysvar1),128k(sysvar2),' +
-              '14m(kernel0),14m(kernel1),-(user_data) debug=1 root=rootfs0 ' +
+      f.write('ttyS0,115200 mtdparts=spi_flash:768k(loader),256k(env),'
+              '128k(var1),128k(var2),128k(sysvar1),128k(sysvar2),'
+              '14m(kernel0),14m(kernel1),-(user_data) debug=1 root=rootfs0 '
               'console=ttyS0,115200 log_buf_len=1048576')
     part = readubootver.GetRootPartition()
     self.assertEqual('kernel0', part)
@@ -46,18 +47,17 @@
     part = readubootver.GetRootPartition()
     self.assertEqual('kernel0', part)
 
-
   def testGetBootMtds(self):
     with open(readubootver.MTD_FILE, 'w') as f:
-      f.write('dev:    size   erasesize  name\n' +
-              'mtd0: 000c0000 00010000 "loader"\n' +
-              'mtd1: 00040000 00010000 "env"\n' +
-              'mtd2: 00020000 00010000 "var1"\n' +
-              'mtd3: 00020000 00010000 "var2"\n' +
-              'mtd4: 00020000 00010000 "sysvar1"\n' +
-              'mtd5: 00020000 00010000 "sysvar2"\n' +
-              'mtd6: 00e00000 00010000 "kernel0"\n' +
-              'mtd7: 00e00000 00010000 "kernel1"\n' +
+      f.write('dev:    size   erasesize  name\n'
+              'mtd0: 000c0000 00010000 "loader"\n'
+              'mtd1: 00040000 00010000 "env"\n'
+              'mtd2: 00020000 00010000 "var1"\n'
+              'mtd3: 00020000 00010000 "var2"\n'
+              'mtd4: 00020000 00010000 "sysvar1"\n'
+              'mtd5: 00020000 00010000 "sysvar2"\n'
+              'mtd6: 00e00000 00010000 "kernel0"\n'
+              'mtd7: 00e00000 00010000 "kernel1"\n'
               'mtd8: 00280000 00010000 "user_data"\n')
     mtds = readubootver.GetBootMtds()
     self.assertEqual(2, len(mtds))
@@ -67,20 +67,19 @@
     self.assertEqual('mtd7', mtds['kernel1'])
 
     with open(readubootver.MTD_FILE, 'w') as f:
-      f.write('dev:    size   erasesize  name\n' +
-              'mtd0: 000c0000 00010000 "loader"\n' +
-              'mtd1: 00040000 00010000 "env"\n' +
-              'mtd2: 00020000 00010000 "var1"\n' +
-              'mtd3: 00020000 00010000 "var2"\n' +
-              'mtd4: 00020000 00010000 "sysvar1"\n' +
-              'mtd5: 00020000 00010000 "sysvar2"\n' +
+      f.write('dev:    size   erasesize  name\n'
+              'mtd0: 000c0000 00010000 "loader"\n'
+              'mtd1: 00040000 00010000 "env"\n'
+              'mtd2: 00020000 00010000 "var1"\n'
+              'mtd3: 00020000 00010000 "var2"\n'
+              'mtd4: 00020000 00010000 "sysvar1"\n'
+              'mtd5: 00020000 00010000 "sysvar2"\n'
               'mtd166: 00e00000 00010000 "kernel0"\n')
     mtds = readubootver.GetBootMtds()
     self.assertEqual(1, len(mtds))
     self.assertTrue('kernel0' in mtds)
     self.assertEqual('mtd166', mtds['kernel0'])
 
-
   def testReadUbootHeader(self):
     data = (0x27051956, 0xdeadf00d, 0x123, 0x1234, 10, 10, 0xf00d,
             0, 0, 0, 0, 'version123')
diff --git a/cmds/soft_rc.py b/cmds/soft_rc.py
index 685a069..37e628d 100755
--- a/cmds/soft_rc.py
+++ b/cmds/soft_rc.py
@@ -412,6 +412,16 @@
         raise
 
   def SendKeyCode(self, token, keycode):
+    """Send key codes to the device.
+
+    If self.autorelease is True, automatically generate a key-up event a short
+    time after each key-down event is sent.
+
+    Args:
+      token: a token for a single key event from the program's input.
+      keycode: the keycode corresponding to this token.
+    """
+
     self.Log(LOG_VERB, "Enter: %r -> 0x%x" % (token, keycode))
     self.WriteKeyCodeToDevice(keycode)
 
diff --git a/cmds/ssdptax-test-server.py b/cmds/ssdptax-test-server.py
index c86283a..de745cb 100644
--- a/cmds/ssdptax-test-server.py
+++ b/cmds/ssdptax-test-server.py
@@ -1,7 +1,5 @@
 #!/usr/bin/python
-"""Fake minissdpd for unit tests.
-
-"""
+"""Fake minissdpd for unit tests."""
 
 import BaseHTTPServer
 import socket
@@ -51,11 +49,11 @@
 
 
 class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-  """Respond to an HHTP GET for SSDP DeviceInfo."""
 
-  def do_GET(self):
+  def do_GET(self):  # pylint: disable=invalid-name
+    """Respond to an HTTP GET for SSDP DeviceInfo."""
     self.send_response(200)
-    self.send_header('Content-type','text/xml')
+    self.send_header('Content-type', 'text/xml')
     self.end_headers()
     if self.path.endswith('text_device_xml'):
       self.wfile.write(text_device_xml)
@@ -67,7 +65,8 @@
       self.wfile.write(ssdp_device_xml)
 
 
-class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
+class ThreadingHTTPServer(SocketServer.ThreadingMixIn,
+                          BaseHTTPServer.HTTPServer):
   pass
 
 
@@ -85,6 +84,7 @@
 
 
 class UdpHandler(SocketServer.DatagramRequestHandler):
+
   def handle(self):
     self.request[1].sendto(bytearray(notify_text[0]), self.client_address)
 
@@ -105,7 +105,7 @@
   if testnum == 4:
     pathend = 'ssdp_device_xml'
 
-  h = ThreadingHTTPServer(("", 0), HttpHandler)
+  h = ThreadingHTTPServer(('', 0), HttpHandler)
   sn = h.socket.getsockname()
   port = sn[1]
   url = 'http://127.0.0.1:%d/%s' % (port, pathend)
@@ -126,7 +126,8 @@
 
   d = ThreadingUdpServer(('', 1900), UdpHandler)
   d.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
-      socket.inet_aton('239.255.255.250') + socket.inet_aton('0.0.0.0'))
+                      socket.inet_pton(socket.AF_INET, '239.255.255.250') +
+                      socket.inet_pton(socket.AF_INET, '0.0.0.0'))
   d_thread = threading.Thread(target=d.serve_forever)
   d_thread.daemon = True
   d_thread.start()
diff --git a/cmds/stun.py b/cmds/stun.py
index 5b162ef..94b45a3 100755
--- a/cmds/stun.py
+++ b/cmds/stun.py
@@ -99,7 +99,7 @@
 
 
 try:
-  import monotime  # pylint: disable-msg=unused-import,g-import-not-at-top
+  import monotime  # pylint: disable=unused-import,g-import-not-at-top
 except ImportError:
   pass
 try:
@@ -130,9 +130,9 @@
   family, port = struct.unpack('!HH', value[:4])
   addr = value[4:]
   if family == 0x01:  # IPv4
-    return socket.inet_ntoa(addr), port
+    return socket.inet_ntop(socket.AF_INET, addr), port
   elif family == 0x02:  # IPv6
-    return socket.inet_ntop(socket.AF_INET6), port
+    return socket.inet_ntop(socket.AF_INET6, addr), port
   else:
     raise ValueError('invalid family %d: expected 1=IPv4 or 2=IPv6' % family)
 
@@ -166,6 +166,7 @@
 
 
 def _Encode(msgtype, transaction_id, *attrs):
+  """Encode message type, transaction id, and attrs into a STUN message."""
   transaction_id = str(transaction_id)
   if len(transaction_id) != 12:
     raise ValueError('transactionid %r must be exactly 12 bytes'
@@ -179,6 +180,7 @@
 
 
 def _Decode(text):
+  """Decode a STUN message into message type, transaction id, and attrs."""
   if len(text) < 20:
     raise ParseError('packet length %d must be >= 20' % len(text))
   msgtype, length, cookie = struct.unpack('!HHI', text[:8])
diff --git a/cmds/test-logos.py b/cmds/test-logos.py
index 34f5693..186ea24 100755
--- a/cmds/test-logos.py
+++ b/cmds/test-logos.py
@@ -2,17 +2,23 @@
 
 """Tests for the logos program."""
 
-import random
+import os
 import select
 import signal
 import socket
 import subprocess
-import time
-from wvtest.wvtest import *
+
+from wvtest.wvtest import WVFAIL
+from wvtest.wvtest import WVPASS
+from wvtest.wvtest import WVPASSEQ
+from wvtest.wvtest import WVPASSLT
+from wvtest.wvtest import wvtest
+from wvtest.wvtest import wvtest_main
 
 
 @wvtest
-def testLogos():
+def TestLogos():
+  """spin up and test a logos server."""
   # We use a SOCK_DGRAM here rather than a normal pipe, because datagram
   # sockets are guaranteed never to merge consecutive writes into a single
   # packet.  That way we can validate the correct merging of write() calls
@@ -30,7 +36,7 @@
   fd2 = sock2.fileno()
 
   def _Read():
-    r, w, x = select.select([fd2, pipe1], [], [], 30)
+    r, unused_w, unused_x = select.select([fd2, pipe1], [], [], 30)
     if pipe1 in r:
       WVFAIL('subprocess died unexpectedly')
       raise Exception('subprocess died unexpectedly with code %d' % p.wait())
@@ -42,7 +48,7 @@
   os.write(fd1, 'a\nErROR: b\nw: c')
   WVPASSEQ('<7>fac: a\n', _Read())
   WVPASSEQ('<3>fac: ErROR: b\n', _Read())
-  r, w, x = select.select([fd2], [], [], 0)
+  r, unused_w, unused_x = select.select([fd2], [], [], 0)
   WVFAIL(r)
   os.write(fd1, '\n\n')
   WVPASSEQ('<4>fac: w: c\n', _Read())
@@ -54,13 +60,13 @@
   WVPASSEQ('<7>fac: abba    bbb\n', _Read())
   WVPASSEQ('<7>fac: aa              b       c\n', _Read())
   os.write(fd1, ''.join(chr(i) for i in range(33)) + '\n')
-  WVPASSEQ(r'<7>fac: ' +
+  WVPASSEQ(r'<7>fac: '
            r'\x00\x01\x02\x03\x04\x05\x06\x07\x08    '
-           + '\n', _Read())
-  WVPASSEQ(r'<7>fac: ' +
-           r'\x0b\x0c\x0e\x0f' +
-           r'\x10\x11\x12\x13\x14\x15\x16\x17' +
-           r'\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' +
+           '\n', _Read())
+  WVPASSEQ(r'<7>fac: '
+           r'\x0b\x0c\x0e\x0f'
+           r'\x10\x11\x12\x13\x14\x15\x16\x17'
+           r'\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f '
            '\n', _Read())
 
   # very long lines should be broken, but not missing any characters
@@ -81,7 +87,7 @@
   result = _Read()
   print '%r' % result
   WVPASS(result.startswith('<4>fac: W: '))
-  r, w, x = select.select([fd2], [], [], 0)
+  r, unused_w, unused_x = select.select([fd2], [], [], 0)
   WVFAIL(r)
   os.write(fd1, '\n')
   WVPASSEQ('<7>fac: booga!\n', _Read())
@@ -94,9 +100,8 @@
     print repr(result)
   print 'got: %r' % result
   WVPASS('rate limiting started')
-  while 1:
-    # drain the input until it's idle
-    r, w, x = select.select([fd1], [], [], 0.1)
+  while 1:  # drain the input until it's idle
+    r, unused_w, unused_x = select.select([fd1], [], [], 0.1)
     if not r:
       break
 
diff --git a/gpio-mailbox/brcm-direct.c b/gpio-mailbox/brcm-direct.c
index 9667db1..b1e6294 100644
--- a/gpio-mailbox/brcm-direct.c
+++ b/gpio-mailbox/brcm-direct.c
@@ -390,8 +390,7 @@
   reg[0] = 0x00;
   reg[1] = 0x57;
 
-  // Default the LED brightness to 50.
-  set_pwm(&p->leds.led_brightness, 50);
+  set_pwm(&p->leds.led_brightness, 27);
 }
 
 static double get_avs_voltage_7252(struct Voltage* v) {
diff --git a/gpio-mailbox/brcm-nexus.c b/gpio-mailbox/brcm-nexus.c
index f559c1b..c53332f 100644
--- a/gpio-mailbox/brcm-nexus.c
+++ b/gpio-mailbox/brcm-nexus.c
@@ -201,8 +201,7 @@
 
   NEXUS_Pwm_CloseChannel(pwm);
 
-  // Default the LED brightness to 50.
-  set_pwm(&p->leds.led_brightness, 50);
+  set_pwm(&p->leds.led_brightness, 27);
 }
 
 void platform_cleanup() {
diff --git a/gpio-mailbox/gpio-mailbox.c b/gpio-mailbox/gpio-mailbox.c
index 9956de7..ea472e7 100644
--- a/gpio-mailbox/gpio-mailbox.c
+++ b/gpio-mailbox/gpio-mailbox.c
@@ -235,7 +235,7 @@
 }
 
 
-static volatile int shutdown_sig = 0;
+static volatile sig_atomic_t shutdown_sig = 0;
 static void sig_handler(int sig) {
   shutdown_sig = sig;
 
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..bdc5f7f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..964f907
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..227be7c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..8e63639
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..7db1059
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..f332fe6
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 2.4GHz OnHub An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..ff860eb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..c6d5226
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz GFRG210 An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..9854b2f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..c860386
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz Google Wifi An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..3a790d2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..373b9c5
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC 10 5GHz OnHub An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Broadcast Probe.pcap
new file mode 100644
index 0000000..8187967
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..1d6a069
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 2.4GHz GFRG210 An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz Google Wifi An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz Google Wifi An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..1c77515
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 2.4GHz Google Wifi An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Broadcast Probe.pcap
new file mode 100644
index 0000000..c89b89b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..a459348
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 2.4GHz OnHub An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 2.4GHz.pcap b/taxonomy/testdata/pcaps/LG G3 2.4GHz.pcap
deleted file mode 100644
index 19002cf..0000000
--- a/taxonomy/testdata/pcaps/LG G3 2.4GHz.pcap
+++ /dev/null
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Broadcast Probe.pcap
new file mode 100644
index 0000000..d3d38d4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..49943f1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz GFRG210 An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Broadcast Probe.pcap
new file mode 100644
index 0000000..aebde75
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..6ee379c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz Google Wifi An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Broadcast Probe.pcap
new file mode 100644
index 0000000..cbf3ba2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Specific Probe.pcap
new file mode 100644
index 0000000..76816e1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G3 5GHz OnHub An5.0 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..6412a6d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..025eb08
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 2.4GHz GFRG210 An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..d748338
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..e9c4275
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 2.4GHz Google Wifi An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G5 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..f3662ce
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 2.4GHz OnHub An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 5GHz GFRG210 An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G5 5GHz GFRG210 An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..2973914
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 5GHz GFRG210 An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..269c3f9
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..207fb20
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 5GHz Google Wifi An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Broadcast Probe.pcap
new file mode 100644
index 0000000..4650b18
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Specific Probe.pcap b/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Specific Probe.pcap
new file mode 100644
index 0000000..577523c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG G5 5GHz OnHub An6.0.1 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index 4057db6..1d7e200 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -40,7 +40,7 @@
     'wifi4|probe:0,1,50,127,107,221(0050f2,4),221(506f9a,9),221(506f9a,16),extcap:00000080,wps:5042T|assoc:0,1,50,45,48,127,221(0050f2,2),htcap:1172,htagg:03,htmcs:000000ff,extcap:01':
         ('Alcatel OneTouch', 'Pop Astro', '2.4GHz'),
 
-    'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:110c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:110c,htagg:19,htmcs:000000ff|os:dashbutton':
+    'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:110c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:110c,htagg:19,htmcs:000000ff|oui:amazon':
         ('Amazon Dash Button', '', '2.4GHz'),
 
     'wifi4|probe:0,1,3,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,48,45,221(0050f2,2),191,127,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000a0200000040|oui:amazon':
@@ -292,6 +292,25 @@
     'wifi4|probe:0,1,50,45,htcap:0130,htagg:18,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),htcap:013c,htagg:18,htmcs:000000ff|oui:htc':
         ('HTC One', 'X', '2.4GHz'),
 
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:000008':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9178b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,70,45,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,70,45,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9178b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9138b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:000008':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:0163,htagg:17,htmcs:0000ffff,vhtcap:0f901032,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9138b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:000008':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9178b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f9118b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:1102,extcap:000008':
+        ('HTC 10', '', '5GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:11ef,htagg:17,htmcs:0000ffff,extcap:00080f8401400040,wps:HTC_M10h|assoc:0,1,50,33,36,48,70,45,221(001018,2),221(0050f2,2),htcap:11ef,htagg:17,htmcs:0000ffff,txpow:1302':
+        ('HTC 10', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:11ef,htagg:17,htmcs:0000ffff,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,50,33,36,48,45,127,221(001018,2),221(0050f2,2),htcap:11ef,htagg:17,htmcs:0000ffff,txpow:1302,extcap:000008':
+        ('HTC 10', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(0050f2,4),221(506f9a,9),221(506f9a,16),221(0050f2,8),221(001018,2),htcap:11ef,htagg:17,htmcs:0000ffff,extcap:00080f840140,wps:HTC_M10h|assoc:0,1,50,33,36,48,70,45,221(001018,2),221(0050f2,2),htcap:11ef,htagg:17,htmcs:0000ffff,txpow:1302':
+        ('HTC 10', '', '2.4GHz'),
+
     'wifi4|probe:0,1,45,221(001018,2),221(00904c,51),htcap:080c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:080c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
         ('iPad', '1st or 2nd gen', '5GHz'),
     'wifi4|probe:0,1,45,221(001018,2),htcap:080c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),htcap:080c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
@@ -689,18 +708,35 @@
         ('LG G3', '', '5GHz'),
     'wifi4|probe:0,1,45,221(0050f2,8),191,127,107,221(506f9a,16),htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:000000800040|assoc:0,1,33,36,48,45,221(0050f2,2),191,127,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:170d,extcap:00000a8201400040|oui:lg':
         ('LG G3', '', '5GHz'),
+    'wifi4|probe:0,1,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,33,36,48,45,221(0050f2,2),191,127,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:170d,extcap:00000a8201400040|oui:lg':
+        ('LG G3', '', '5GHz'),
+    'wifi4|probe:0,1,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,48,45,221(0050f2,2),191,127,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000a8201400040|oui:lg':
+        ('LG G3', '', '5GHz'),
+    'wifi4|probe:0,1,45,221(0050f2,8),191,127,107,221(506f9a,16),htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:000000800040|assoc:0,1,48,45,221(0050f2,2),191,127,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000a8201400040|oui:lg':
+        ('LG G3', '', '5GHz'),
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,107,221(506f9a,16),htcap:012c,htagg:03,htmcs:000000ff,extcap:000000800040|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a8201400000|oui:lg':
         ('LG G3', '', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,107,221(506f9a,16),htcap:012c,htagg:03,htmcs:000000ff,extcap:000000800040|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a8201400000|oui:lg':
         ('LG G3', '', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,107,221(506f9a,16),htcap:016e,htagg:03,htmcs:000000ff,extcap:000000800040|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a8201400000|oui:lg':
         ('LG G3', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a8201400000|oui:lg':
+        ('LG G3', '', '2.4GHz'),
 
     'wifi4|probe:0,1,3,45,127,107,191,221(506f9a,16),221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0000088001400040|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(00904c,4),221(0050f2,2),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:1d01,extcap:0000008001400040|oui:lg':
         ('LG G4', '', '5GHz'),
     'wifi4|probe:0,1,50,45,127,107,221(506f9a,16),221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:112d,htagg:17,htmcs:000000ff,extcap:0000088001400040|assoc:0,1,33,36,48,50,45,127,221(001018,2),221(0050f2,2),htcap:112d,htagg:17,htmcs:000000ff,txpow:1001,extcap:000000800140|oui:lg':
         ('LG G4', '', '2.4GHz'),
 
+    'wifi4|probe:0,1,45,127,107,191,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0000088001400040|assoc:0,1,33,36,48,45,127,191,221(00904c,4),221(001632,64),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:e003,extcap:0000080000000040|oui:lg':
+        ('LG G5', '', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0000088001400040|assoc:0,1,33,36,48,70,45,127,191,221(00904c,4),221(001632,64),221(001018,2),221(0050f2,2),htcap:0063,htagg:17,htmcs:000000ff,vhtcap:0f805032,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:e003,extcap:0000000000000040|oui:lg':
+        ('LG G5', '', '5GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0000088001400040|assoc:0,1,50,33,36,48,45,127,221(00904c,4),221(001632,64),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:0f03,extcap:0000080000000040|oui:lg':
+        ('LG G5', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:0021,htagg:17,htmcs:000000ff,extcap:0000088001400040|assoc:0,1,50,33,36,48,70,45,127,221(001632,64),221(001018,2),221(0050f2,2),htcap:0021,htagg:17,htmcs:000000ff,txpow:0f03,extcap:0000000000000040|oui:lg':
+        ('LG G5', '', '2.4GHz'),
+
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),221(0050f2,4),221(506f9a,9),htcap:012c,htagg:03,htmcs:000000ff,wps:LGL16C|assoc:0,1,50,48,45,221(0050f2,2),htcap:012c,htagg:03,htmcs:000000ff':
         ('LG Lucky', '', '2.4GHz'),
 
diff --git a/wifi/configs.py b/wifi/configs.py
index 4dcdaab..e83ee68 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -2,16 +2,18 @@
 
 """Utils for hostapd and wpa_supplicant configuration."""
 
+import ctypes
 import subprocess
 
-import Crypto.Protocol.KDF
-
 # pylint: disable=g-bad-import-order
 import autochannel
 import experiment
 import utils
 
 
+crypto = ctypes.CDLL('libcrypto.so')
+
+
 EXPERIMENTS = [
     'NoSwapWifiPrimaryChannel',  # checked by hostapd itself
     'NoAutoNarrowWifiChannel',  # checked by hostapd itself
@@ -357,8 +359,11 @@
   if len(clean_passphrase) == 64:
     network_lines += ['\tpsk=%s' % clean_passphrase]
   else:
-    raw_psk = Crypto.Protocol.KDF.PBKDF2(clean_passphrase, clean_ssid, 32, 4096)
-    hex_psk = ''.join(ch.encode('hex') for ch in raw_psk)
+    psk_buf = ctypes.create_string_buffer(32)
+    crypto.PKCS5_PBKDF2_HMAC_SHA1(clean_passphrase, len(clean_passphrase),
+                                  clean_ssid, len(clean_ssid),
+                                  4096, 32, psk_buf)
+    hex_psk = ''.join(ch.encode('hex') for ch in psk_buf.raw)
     network_lines += ['\t#psk="%s"' % clean_passphrase, '\tpsk=%s' % hex_psk]
 
   return network_lines
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 9e0d26b..3e6445f 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -58,11 +58,8 @@
 def _parse_scan_result(line):
   # Scan result format:
   #
-  # "Quantenna1" 00:26:86:00:11:5f 60 56 1 2 1 2 0 15 80 100 1 Infrastructure
-  # |            |                 |  |  | | | | | |  |  |   | |
-  # |            |                 |  |  | | | | | |  |  |   | Mode
-  # |            |                 |  |  | | | | | |  |  |   DTIM interval
-  # |            |                 |  |  | | | | | |  |  Beacon interval
+  # "Quantenna1" 00:26:86:00:11:5f 60 56 1 2 1 2 0 15 80
+  # |            |                 |  |  | | | | | |  |
   # |            |                 |  |  | | | | | |  Maximum bandwidth
   # |            |                 |  |  | | | | | WPS flags
   # |            |                 |  |  | | | | Qhop flags
@@ -77,7 +74,7 @@
   #
   # The SSID may contain quotes and spaces. Split on whitespace from the right,
   # making at most 10 splits, to preserve spaces in the SSID.
-  sp = line.strip().rsplit(None, 13)
+  sp = line.strip().rsplit(None, 10)
   return sp[0][1:-1], sp[1], int(sp[2]), -float(sp[3]), int(sp[4]), int(sp[5])
 
 
diff --git a/wifi/quantenna_test.py b/wifi/quantenna_test.py
index 00400ed..beef431 100755
--- a/wifi/quantenna_test.py
+++ b/wifi/quantenna_test.py
@@ -33,8 +33,7 @@
 
 @wvtest.wvtest
 def parse_scan_result_test():
-  result = ('  " ssid with "quotes" " 00:11:22:33:44:55 40 25 0 0 0 0 0 1 40 '
-            '100 1 Infrastructure')
+  result = '  " ssid with "quotes" " 00:11:22:33:44:55 40 25 0 0 0 0 0 1 40  '
   wvtest.WVPASSEQ(quantenna._parse_scan_result(result),
                   (' ssid with "quotes" ', '00:11:22:33:44:55', 40, -25, 0, 0))
 
diff --git a/wifi/utils.py b/wifi/utils.py
index c3b237e..198bf16 100644
--- a/wifi/utils.py
+++ b/wifi/utils.py
@@ -120,8 +120,9 @@
 
 def increment_mac_address(mac_address):
   numeric_mac_address = int(''.join(mac_address.split(':')), 16) + 1
-  return ':'.join(octet.encode('hex')
-                  for octet in ('%x' % numeric_mac_address).decode('hex'))
+  numeric_mac_address %= 2 ** 48
+  octets = ('%012x' % numeric_mac_address).decode('hex')
+  return ':'.join(octet.encode('hex') for octet in octets)
 
 
 def get_filename(program, kind, disambiguator, tmp=False):
diff --git a/wifi/utils_test.py b/wifi/utils_test.py
index 0224605..8febea3 100755
--- a/wifi/utils_test.py
+++ b/wifi/utils_test.py
@@ -83,10 +83,20 @@
 
 @wvtest.wvtest
 def increment_mac_address_test():
-  wvtest.WVPASSEQ('12:34:56:78:91',
-                  utils.increment_mac_address('12:34:56:78:90'))
-  wvtest.WVPASSEQ('12:34:56:79:00',
-                  utils.increment_mac_address('12:34:56:78:FF'))
+  """Test increment_mac_address."""
+
+  wvtest.WVPASSEQ('12:34:56:78:90:13',
+                  utils.increment_mac_address('12:34:56:78:90:12'))
+  wvtest.WVPASSEQ('12:34:56:78:91:00',
+                  utils.increment_mac_address('12:34:56:78:90:FF'))
+
+  # b/34050122
+  wvtest.WVPASSEQ('00:0b:6b:ed:eb:ad',
+                  utils.increment_mac_address('00:0b:6b:ed:eb:ac'))
+
+  # b/34050122 (initial misunderstanding of bug, but still worth testing)
+  wvtest.WVPASSEQ('00:00:00:00:00:00',
+                  utils.increment_mac_address('ff:ff:ff:ff:ff:ff'))
 
 
 @wvtest.wvtest