Merge "conman:  Determine client connection status using key_mgmt."
diff --git a/cache_warming/cache_warming.py b/cache_warming/cache_warming.py
index 5ef444c..986bcd7 100644
--- a/cache_warming/cache_warming.py
+++ b/cache_warming/cache_warming.py
@@ -56,7 +56,14 @@
   if os.path.isfile(HOSTS_JSON_PATH):
     with open(HOSTS_JSON_PATH, 'r') as hosts_json:
       global hit_log
-      hit_log = json.load(hosts_json)
+      try:
+        hit_log = json.load(hosts_json)
+      except ValueError as e:
+        if verbose:
+          print 'Failed to open %s: %s.' % (HOSTS_JSON_PATH, e)
+      finally:
+        if not isinstance(hit_log, dict):
+          hit_log = {}
 
 
 def process_query(qry):
@@ -171,13 +178,7 @@
   sys.stderr = os.fdopen(2, 'w', 1)
   args = set_args()
   verbose = args.verbose
-  try:
-    load_hosts()
-  except ValueError as e:
-    if verbose:
-      print 'Failed to open %s: %s.' % (HOSTS_JSON_PATH, e)
-    save_hosts(hit_log)
-
+  load_hosts()
   server_address = UDP_SERVER_PATH
   try:
     os.remove(server_address)
diff --git a/cache_warming/cache_warming_test.py b/cache_warming/cache_warming_test.py
index 220edc1..ceed91b 100644
--- a/cache_warming/cache_warming_test.py
+++ b/cache_warming/cache_warming_test.py
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 """Tests for cache_warming.py."""
 
+import os
 import cache_warming
 from wvtest import wvtest
 
@@ -72,5 +73,55 @@
   wvtest.WVPASSEQ(actual, expected)
 
 
+@wvtest.wvtest
+def test_no_cache_warming_hosts():
+  crash = False
+  cache_warming.HOSTS_JSON_PATH = '/tmp/cache_warming_hosts.json'
+  if os.path.exists('/tmp/cache_warming_hosts.json'):
+    os.remove('/tmp/cache_warming_hosts.json')
+  try:
+    cache_warming.load_hosts()
+    cache_warming.warm_cache(53, None)
+  except ValueError:
+    crash = True
+  wvtest.WVFAIL(crash)
+
+
+@wvtest.wvtest
+def test_empty_cache_warming_hosts():
+  crash = False
+  cache_warming.HOSTS_JSON_PATH = '/tmp/cache_warming_hosts.json'
+  if os.path.exists('/tmp/cache_warming_hosts.json'):
+    os.remove('/tmp/cache_warming_hosts.json')
+  open('/tmp/cache_warming_hosts.json', 'w').close()
+  try:
+    cache_warming.load_hosts()
+    cache_warming.warm_cache(53, None)
+  except ValueError:
+    crash = True
+  finally:
+    os.remove('/tmp/cache_warming_hosts.json')
+  wvtest.WVFAIL(crash)
+
+
+@wvtest.wvtest
+def test_wrong_cache_warming_hosts():
+  crash = False
+  cache_warming.HOSTS_JSON_PATH = '/tmp/cache_warming_hosts.json'
+  if os.path.exists('/tmp/cache_warming_hosts.json'):
+    os.remove('/tmp/cache_warming_hosts.json')
+  f = open('/tmp/cache_warming_hosts.json', 'w')
+  f.write('[]')
+  f.close()
+  try:
+    cache_warming.load_hosts()
+    cache_warming.warm_cache(53, None)
+  except ValueError:
+    crash = True
+  finally:
+    os.remove('/tmp/cache_warming_hosts.json')
+  wvtest.WVFAIL(crash)
+
+
 if __name__ == '__main__':
   wvtest.wvtest_main()
diff --git a/cmds/Makefile b/cmds/Makefile
index 60c1b5e..3d53395 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -63,13 +63,15 @@
 endif
 
 ifeq ($(BUILD_SSDP),y)
-TARGETS += ssdptax
+TARGETS += ssdptax dialcheck
 HOST_TEST_TARGETS += host-test-ssdptax.sh
+HOST_TEST_TARGETS += host-test-dialcheck.sh
 endif
 
 ifeq ($(BUILD_DNSSD),y)
 # Don't bother building for host
 ARCH_TARGETS += dnssd_hosts
+SCRIPT_TARGETS += castcheck
 endif
 
 ifeq ($(BUILD_IBEACON),y)
@@ -116,6 +118,7 @@
 	for n in $(SCRIPT_TARGETS); do \
 		test ! -f $$n.$(BR2_TARGET_GENERIC_PLATFORM_NAME) || \
 			cp -f $$n.$(BR2_TARGET_GENERIC_PLATFORM_NAME) $(BINDIR)/$$n; \
+		test ! -f $$n || cp -f $$n $(BINDIR)/$$n; \
 	done
 
 install-libs:
@@ -202,6 +205,10 @@
 ssdptax: LIBS += -lcurl -lnl-3 -lstdc++ -lm
 host-ssdptax: host-ssdptax.o host-l2utils.o
 host-ssdptax: LIBS += $(HOST_LIBS) -lcurl -lnl-3 -lstdc++ -lm
+dialcheck: dialcheck.o
+dialcheck: LIBS += -lstdc++ -lm
+host-dialcheck: host-dialcheck.o
+host-dialcheck: LIBS += $(HOST_LIBS) -lstdc++ -lm
 statpitcher.o: device_stats.pb.o
 statpitcher: LIBS+=-L$(DESTDIR)$(PREFIX)/usr/lib -lprotobuf-lite -lpthread -lstdc++
 statpitcher: device_stats.pb.o statpitcher.o
diff --git a/cmds/avahi-browse-fake.sh b/cmds/avahi-browse-fake.sh
new file mode 100755
index 0000000..c7ed603
--- /dev/null
+++ b/cmds/avahi-browse-fake.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+echo '+;br0;IPv4;GFiber\032TV\032Box1001;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1001;_googlecast._tcp;local;GFiber\032TV\032Box1001.local;1.1.1.1;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1001" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
+echo '+;br0;IPv4;GFiber\032TV\032Box1002;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1002;_googlecast._tcp;local;GFiber\032TV\032Box1002.local;3.3.3.3;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1002" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
+echo '+;br0;IPv4;GFiber\032TV\032Box1003;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1003;_googlecast._tcp;local;GFiber\032TV\032Box1003.local;2.2.2.2;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1003" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
diff --git a/cmds/castcheck b/cmds/castcheck
new file mode 100755
index 0000000..70080ec
--- /dev/null
+++ b/cmds/castcheck
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+AVAHI=avahi-browse
+
+while getopts "a:" option
+do
+  case $option in
+  a) AVAHI="$OPTARG" ;;
+  esac
+done
+
+cast_devices=
+while IFS=";" read ip; do
+  cast_devices="$cast_devices $ip"
+done<<EOT
+$($AVAHI -tpvlr _googlecast._tcp | grep "^=" | cut -d";" -f8 | sort)
+EOT
+
+echo "Cast responses from:$cast_devices"
diff --git a/cmds/dialcheck-test-server.py b/cmds/dialcheck-test-server.py
new file mode 100644
index 0000000..2637384
--- /dev/null
+++ b/cmds/dialcheck-test-server.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+"""Fake SSDP server for unit tests.
+
+"""
+
+import errno
+import os
+import signal
+import socket
+import SocketServer
+import struct
+import sys
+
+
+notify = """LOCATION: http://1.1.1.1:1/test.xml\r\n
+CACHE-CONTROL: max-age=1800\r\n
+EXT:\r\n
+SERVER: test_ssdp/1.0\r\n
+ST: urn:dial-multiscreen-org:service:dial:1\r\n
+USN: uuid:number::urn:dial-multiscreen-org:service:dial:1\r\n"""
+
+
+class SSDPHandler(SocketServer.BaseRequestHandler):
+  def handle(self):
+    self.request[1].sendto(notify, self.client_address)
+
+
+def check_pid(pid):
+  try:
+    os.kill(pid, 0)
+  except OSError as e:
+    if e.errno == errno.ESRCH:
+      return False
+  return True
+
+
+def timeout(unused_signum, unused_frame):
+  ppid = os.getppid()
+  if ppid == 1 or not check_pid(ppid):
+    print 'timed out!'
+    sys.exit(2)
+  else:
+    signal.alarm(1)
+
+
+def main():
+  signal.signal(signal.SIGALRM, timeout)
+  signal.alarm(1)
+  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'))
+  sn = s.socket.getsockname()
+  port = sn[1]
+  open(sys.argv[1], "w").write(str(port))
+  s.handle_request()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/cmds/dialcheck.cc b/cmds/dialcheck.cc
new file mode 100644
index 0000000..17f8fbd
--- /dev/null
+++ b/cmds/dialcheck.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+/*
+ * dialcheck
+ *
+ * Check for nearby devices supporting the DIAL protocol.
+ */
+
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <set>
+#include <tr1/unordered_map>
+
+
+typedef std::set<std::string> ResultsSet;
+int timeout_secs = 10;
+
+
+/* SSDP Discover packet */
+int ssdp_port = 1900;
+int ssdp_loop = 0;
+#define SSDP_IP4 "239.255.255.250"
+#define SSDP_IP6 "FF02::C"
+const char discover_template[] = "M-SEARCH * HTTP/1.1\r\n"
+    "HOST: %s:%d\r\n"
+    "MAN: \"ssdp:discover\"\r\n"
+    "MX: 2\r\n"
+    "USER-AGENT: dialcheck/1.0\r\n"
+    "ST: urn:dial-multiscreen-org:service:dial:1\r\n\r\n";
+
+
+int get_ipv4_ssdp_socket()
+{
+  int s;
+  int reuse = 1;
+  struct sockaddr_in sin;
+  struct ip_mreq mreq;
+  struct ip_mreqn mreqn;
+
+  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    perror("socket SOCK_DGRAM");
+    exit(1);
+  }
+
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+    perror("setsockopt SO_REUSEADDR");
+    exit(1);
+  }
+
+  if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
+        &ssdp_loop, sizeof(ssdp_loop))) {
+    perror("setsockopt IP_MULTICAST_LOOP");
+    exit(1);
+  }
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(ssdp_port);
+  sin.sin_addr.s_addr = INADDR_ANY;
+  if (bind(s, (struct sockaddr*)&sin, sizeof(sin))) {
+    perror("bind");
+    exit(1);
+  }
+
+  memset(&mreqn, 0, sizeof(mreqn));
+  mreqn.imr_ifindex = if_nametoindex("br0");
+  if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) {
+    perror("IP_MULTICAST_IF");
+    exit(1);
+  }
+
+  memset(&mreq, 0, sizeof(mreq));
+  mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP4);
+  if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+        (char *)&mreq, sizeof(mreq))) {
+    perror("IP_ADD_MEMBERSHIP");
+    exit(1);
+  }
+
+  return s;
+}
+
+
+void send_ssdp_ip4_request(int s)
+{
+  struct sockaddr_in sin;
+  char buf[1024];
+  ssize_t len;
+
+  snprintf(buf, sizeof(buf), discover_template, SSDP_IP4, ssdp_port);
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(ssdp_port);
+  sin.sin_addr.s_addr = inet_addr(SSDP_IP4);
+  len = strlen(buf);
+  if (sendto(s, buf, len, 0, (struct sockaddr*)&sin, sizeof(sin)) != len) {
+    perror("sendto multicast IPv4");
+    exit(1);
+  }
+}
+
+
+int get_ipv6_ssdp_socket()
+{
+  int s;
+  int reuse = 1;
+  int loop = 0;
+  struct sockaddr_in6 sin6;
+  struct ipv6_mreq mreq;
+  int idx;
+  int hops;
+
+  if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+    perror("socket SOCK_DGRAM");
+    exit(1);
+  }
+
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+    perror("setsockopt SO_REUSEADDR");
+    exit(1);
+  }
+
+  if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop))) {
+    perror("setsockopt IPV6_MULTICAST_LOOP");
+    exit(1);
+  }
+
+  memset(&sin6, 0, sizeof(sin6));
+  sin6.sin6_family = AF_INET6;
+  sin6.sin6_port = htons(ssdp_port);
+  sin6.sin6_addr = in6addr_any;
+  if (bind(s, (struct sockaddr*)&sin6, sizeof(sin6))) {
+    perror("bind");
+    exit(1);
+  }
+
+  idx = if_nametoindex("br0");
+  if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx))) {
+    perror("IP_MULTICAST_IF");
+    exit(1);
+  }
+
+  hops = 2;
+  if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops))) {
+    perror("IPV6_MULTICAST_HOPS");
+    exit(1);
+  }
+
+  memset(&mreq, 0, sizeof(mreq));
+  mreq.ipv6mr_interface = idx;
+  if (inet_pton(AF_INET6, SSDP_IP6, &mreq.ipv6mr_multiaddr) != 1) {
+    fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+    exit(1);
+  }
+  if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
+    perror("ERR: setsockopt(IPV6_JOIN_GROUP)");
+    exit(1);
+  }
+
+  return s;
+}
+
+
+void send_ssdp_ip6_request(int s)
+{
+  struct sockaddr_in6 sin6;
+  char buf[1024];
+  ssize_t len;
+
+  snprintf(buf, sizeof(buf), discover_template, SSDP_IP6, ssdp_port);
+  memset(&sin6, 0, sizeof(sin6));
+  sin6.sin6_family = AF_INET6;
+  sin6.sin6_port = htons(ssdp_port);
+  if (inet_pton(AF_INET6, SSDP_IP6, &sin6.sin6_addr) != 1) {
+    fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+    exit(1);
+  }
+  len = strlen(buf);
+  if (sendto(s, buf, len, 0, (struct sockaddr*)&sin6, sizeof(sin6)) != len) {
+    perror("sendto multicast IPv6");
+    exit(1);
+  }
+}
+
+
+std::string handle_ssdp_response(int s, int family)
+{
+  char buffer[4096];
+  char ipbuf[INET6_ADDRSTRLEN];
+  ssize_t pktlen;
+  struct sockaddr from;
+  socklen_t len = sizeof(from);
+
+  pktlen = recvfrom(s, buffer, sizeof(buffer), 0, &from, &len);
+  if (pktlen <= 0) {
+    return std::string("");
+  }
+
+  if (family == AF_INET) {
+    struct sockaddr_in *sin = (struct sockaddr_in *)&from;
+    inet_ntop(AF_INET, &sin->sin_addr, ipbuf, sizeof(ipbuf));
+    return std::string(ipbuf);
+  } else if (family == AF_INET6) {
+    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&from;
+    inet_ntop(AF_INET6, &sin6->sin6_addr, ipbuf, sizeof(ipbuf));
+    return std::string(ipbuf);
+  }
+
+  return std::string("");
+}
+
+
+/* Wait for SSDP NOTIFY messages to arrive. */
+ResultsSet listen_for_responses(int s4, int s6)
+{
+  ResultsSet results;
+  struct timeval tv;
+  fd_set rfds;
+  int maxfd = (s4 > s6) ? s4 : s6;
+
+  memset(&tv, 0, sizeof(tv));
+  tv.tv_sec = timeout_secs;
+  tv.tv_usec = 0;
+
+  FD_ZERO(&rfds);
+  FD_SET(s4, &rfds);
+  FD_SET(s6, &rfds);
+
+  while (select(maxfd + 1, &rfds, NULL, NULL, &tv) > 0) {
+    if (FD_ISSET(s4, &rfds)) {
+      std::string ip = handle_ssdp_response(s4, AF_INET);
+      if (!ip.empty()) {
+        results.insert(ip);
+      }
+    }
+    if (FD_ISSET(s6, &rfds)) {
+      std::string ip = handle_ssdp_response(s6, AF_INET6);
+      if (!ip.empty()) {
+        results.insert(ip);
+      }
+    }
+
+    FD_ZERO(&rfds);
+    FD_SET(s4, &rfds);
+    FD_SET(s6, &rfds);
+  }
+
+  return results;
+}
+
+
+void usage(char *progname) {
+  fprintf(stderr, "usage: %s [-t port]\nwhere:\n", progname);
+  fprintf(stderr, "\t-t port:  test mode, send to localhost port\n");
+  exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+  int c;
+  int s4, s6;
+
+  setlinebuf(stdout);
+  alarm(30);
+
+  while ((c = getopt(argc, argv, "t:")) != -1) {
+    switch(c) {
+      case 't':
+        timeout_secs = 1;
+        ssdp_port = atoi(optarg);
+        ssdp_loop = 1;
+        break;
+      default: usage(argv[0]); break;
+    }
+  }
+
+  s4 = get_ipv4_ssdp_socket();
+  send_ssdp_ip4_request(s4);
+  s6 = get_ipv6_ssdp_socket();
+  send_ssdp_ip6_request(s6);
+  ResultsSet IPs = listen_for_responses(s4, s6);
+
+  std::string output("DIAL responses from: ");
+  for (ResultsSet::const_iterator ii = IPs.begin(); ii != IPs.end(); ++ii) {
+    output.append(*ii);
+    output.append(" ");
+  }
+  std::cout << output << std::endl;
+
+  exit(0);
+}
diff --git a/cmds/host-test-dialcheck.sh b/cmds/host-test-dialcheck.sh
new file mode 100755
index 0000000..2eb8a8a
--- /dev/null
+++ b/cmds/host-test-dialcheck.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 Google Inc. All Rights Reserved.
+
+. ./wvtest/wvtest.sh
+
+PORTFILE="/tmp/dialcheck.test.$$.port"
+OUTFILE="/tmp/dialcheck.test.$$.output"
+
+WVSTART "dialcheck test"
+
+rm -f "$PORTFILE" "$OUTFILE"
+python ./dialcheck-test-server.py "$PORTFILE" &
+for i in $(seq 50); do if [ ! -f "$PORTFILE" ]; then sleep 0.1; fi; done
+
+port=$(cat "$PORTFILE")
+# Dial response will come from the IP address of the builder.
+WVPASS ./host-dialcheck -t "$port" >"$OUTFILE"
+WVPASS grep "DIAL responses from: " "$OUTFILE"
+rm -f "$PORTFILE" "$OUTFILE"
diff --git a/cmds/test-castcheck.sh b/cmds/test-castcheck.sh
new file mode 100755
index 0000000..7aa3be2
--- /dev/null
+++ b/cmds/test-castcheck.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+. ./wvtest/wvtest.sh
+
+WVSTART "castcheck test"
+CASTCHECK="./castcheck -a ./avahi-browse-fake.sh"
+
+WVPASSEQ "$($CASTCHECK)" "Cast responses from: 1.1.1.1 2.2.2.2 3.3.3.3"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index de8a316..e7ab9af 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -194,6 +194,7 @@
   IP_LINK = ['ip', 'link']
   IFPLUGD_ACTION = ['/etc/ifplugd/ifplugd.action']
   BINWIFI = ['wifi']
+  UPLOAD_LOGS_AND_WAIT = ['timeout', '60', 'upload-logs-and-wait']
 
   def __init__(self,
                bridge_interface='br0',
@@ -218,6 +219,7 @@
     self._acs_update_wait_s = acs_update_wait_s
     self._bssid_cycle_length_s = bssid_cycle_length_s
     self._wlan_configuration = {}
+    self._try_to_upload_logs = False
 
     # Make sure all necessary directories exist.
     for directory in (self._tmp_dir, self._config_dir, self._moca_tmp_dir,
@@ -467,6 +469,10 @@
         self._status.connected_to_wlan = False
         if self.acs():
           logging.debug('Connected to ACS on %s', wifi.name)
+          if self._try_to_upload_logs:
+            self._try_upload_logs()
+            self._try_to_upload_logs = False
+
           wifi.last_successful_bss_info = getattr(wifi,
                                                   'last_attempted_bss_info',
                                                   None)
@@ -695,6 +701,7 @@
         wifi.waiting_for_acs_since = now
         wifi.complain_about_acs_at = now + 5
         logging.info('Attempting to provision via SSID %s', bss_info.ssid)
+        self._try_to_upload_logs = True
       # If we can no longer connect to this, it's no longer successful.
       elif bss_info == last_successful_bss_info:
         wifi.last_successful_bss_info = None
@@ -790,6 +797,11 @@
     subprocess.check_output(self.BINWIFI + list(command),
                             stderr=subprocess.STDOUT)
 
+  def _try_upload_logs(self):
+    logging.debug('Attempting to upload logs')
+    if subprocess.call(self.UPLOAD_LOGS_AND_WAIT) != 0:
+      logging.error('Failed to upload logs')
+
 
 def _wifi_show():
   try:
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index fdbca3c..eba9f47 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -265,6 +265,7 @@
   IFUP = ['echo', 'ifup']
   IFPLUGD_ACTION = ['echo', 'ifplugd.action']
   BINWIFI = ['echo', 'wifi']
+  UPLOAD_LOGS_AND_WAIT = ['echo', 'upload-logs-and-wait']
 
   def __init__(self, *args, **kwargs):
     self._binwifi_commands = []
@@ -297,6 +298,7 @@
     self.can_connect_to_s3 = True
     # Will s2 fail rather than providing ACS access?
     self.s2_fail = False
+    self.log_upload_count = 0
 
   def create_wifi_interfaces(self):
     super(ConnectionManager, self).create_wifi_interfaces()
@@ -406,6 +408,10 @@
 
     return self._wlan_configuration[band].client_up
 
+  def _try_upload_logs(self):
+    self.log_upload_count += 1
+    return super(ConnectionManager, self)._try_upload_logs()
+
   # Test methods
 
   def delete_wlan_config(self, band):
@@ -658,6 +664,7 @@
   c.interface_with_scan_results = c.wifi_for_band(band).name
   # Wait for a scan, plus 3 cycles, so that s2 will have been tried.
   c.run_until_scan(band)
+  wvtest.WVPASSEQ(c.log_upload_count, 0)
   for _ in range(3):
     c.run_once()
     wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
@@ -672,6 +679,7 @@
   wvtest.WVPASS(c.internet())
   wvtest.WVFAIL(c.client_up(band))
   wvtest.WVPASS(c.wifi_for_band(band).current_route())
+  wvtest.WVPASSEQ(c.log_upload_count, 1)
   # Disable scan results again.
   c.interface_with_scan_results = None
 
@@ -711,6 +719,7 @@
   wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
   wvtest.WVPASSEQ(c.last_provisioning_attempt.ssid, 's3')
   wvtest.WVPASSEQ(c.last_provisioning_attempt.bssid, 'ff:ee:dd:cc:bb:aa')
+  wvtest.WVPASSEQ(c.log_upload_count, 2)
 
   # Now, recreate the same WLAN configuration, which should be connected to.
   # Also, test that atomic writes/renames work.
@@ -778,6 +787,7 @@
   wvtest.WVPASS(c.acs())
   # Make sure we didn't scan on `band`.
   wvtest.WVPASSEQ(scan_count_for_band, c.wifi_for_band(band).wifi_scan_counter)
+  wvtest.WVPASSEQ(c.log_upload_count, 3)
 
   # Now re-create the WLAN config, connect to the WLAN, and make sure that s3 is
   # unset as last_successful_bss_info, since it is no longer available.
@@ -820,6 +830,7 @@
     c.run_once()
   s2_bss = iw.BssInfo('01:23:45:67:89:ab', 's2')
   wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, s2_bss)
+  wvtest.WVPASSEQ(c.log_upload_count, 4)
 
   c.s2_fail = True
   c.write_wlan_config(band, ssid, psk)
@@ -972,6 +983,7 @@
   wvtest.WVFAIL(c.bridge.current_route())
   wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
   wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+  wvtest.WVPASSEQ(c.log_upload_count, 1)
 
 
 @wvtest.wvtest
@@ -1064,6 +1076,7 @@
   wvtest.WVFAIL(c.bridge.current_route())
   wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
   wvtest.WVPASS(c.wifi_for_band('5').current_route())
+  wvtest.WVPASSEQ(c.log_upload_count, 1)
 
 
 @wvtest.wvtest
diff --git a/gpio-mailbox/TEST.gpio-mailbox b/gpio-mailbox/TEST.gpio-mailbox
index 3cd5dee..edc8f60 100644
--- a/gpio-mailbox/TEST.gpio-mailbox
+++ b/gpio-mailbox/TEST.gpio-mailbox
@@ -1,4 +1,4 @@
-rm -rf /tmp/gpio /tmp/led
+rm -rf /tmp/gpio /tmp/leds
 
 mkdir -p /tmp/gpio
 echo x5 0 1 0 2 0 0x0f > /tmp/gpio/leds
diff --git a/gpio-mailbox/broadcom.c b/gpio-mailbox/broadcom.c
index 575f197..a53e51e 100644
--- a/gpio-mailbox/broadcom.c
+++ b/gpio-mailbox/broadcom.c
@@ -614,6 +614,15 @@
   }
 }
 
+static void *mmap_(void* addr, size_t size, int prot, int flags, int fd,
+                   off_t offset) {
+#ifdef __ANDROID__
+  return mmap64(addr, size, prot, flags, fd, (off64_t)(uint64_t)(uint32_t)offset);
+#else
+  return mmap(addr, size, prot, flags, fd, offset);
+#endif
+}
+
 static int platform_init(struct platform_info* p) {
   platform_cleanup();
 
@@ -623,8 +632,8 @@
     return -1;
   }
   mmap_size = p->mmap_size;
-  mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
-                   mmap_fd, p->mmap_base);
+  mmap_addr = mmap_(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                    mmap_fd, p->mmap_base);
   if (mmap_addr == MAP_FAILED) {
     perror("mmap");
     platform_cleanup();
diff --git a/gpio-mailbox/gfch100.c b/gpio-mailbox/gfch100.c
index 73d97bd..2cf1f23 100644
--- a/gpio-mailbox/gfch100.c
+++ b/gpio-mailbox/gfch100.c
@@ -18,15 +18,13 @@
 #define GPIO_OUT                "out"
 
 /* GPIO_ACTIVITY LED is blue on Chimera. */
-#define GPIO_ACTIVITY           "30"
-#define GPIO_RED                "31"
+#define GPIO_ACTIVITY		"/led_activity"
+#define GPIO_RED		"/led_red"
 
-#define GPIO_BASE_DIR           "/sys/class/gpio"
-#define GPIO_EXPORT             GPIO_BASE_DIR "/export"
+#define GPIO_BASE_DIR		"/dev/gpio"
 
-#define GPIO_DIR(n)             GPIO_BASE_DIR "/gpio" n
+#define GPIO_DIR(n)             GPIO_BASE_DIR n
 
-#define GPIO_DIRECTION(dir)     dir "/direction"
 #define GPIO_VALUE(dir)         dir "/value"
 
 struct PinHandle_s {
@@ -38,9 +36,7 @@
 };
 
 struct sysgpio {
-  const char* export_value;
   const char* value_path;
-  const char* direction_path;
 };
 
 struct platform_info {
@@ -57,13 +53,9 @@
       .value_path = "/sys/class/hwmon/hwmon0/temp1_input",
     },
     .led_red = {
-      .export_value = GPIO_RED,
-      .direction_path = GPIO_DIRECTION(GPIO_DIR(GPIO_RED)),
       .value_path = GPIO_VALUE(GPIO_DIR(GPIO_RED)),
     },
     .led_activity = {
-      .export_value = GPIO_ACTIVITY,
-      .direction_path = GPIO_DIRECTION(GPIO_DIR(GPIO_ACTIVITY)),
       .value_path = GPIO_VALUE(GPIO_DIR(GPIO_ACTIVITY)),
     },
   }
@@ -89,16 +81,6 @@
     perror("calloc(PinHandle)");
     return NULL;
   }
-
-  // initialize leds to match boot values
-  write_file_string(GPIO_EXPORT, GPIO_RED);
-  write_file_string(platform->led_red.direction_path, GPIO_OUT);
-  write_file_string(platform->led_red.value_path, GPIO_OFF);
-
-  write_file_string(GPIO_EXPORT, GPIO_ACTIVITY);
-  write_file_string(platform->led_activity.direction_path, GPIO_OUT);
-  write_file_string(platform->led_activity.value_path, GPIO_ON);
-
   return handle;
 }
 
diff --git a/taxonomy/dhcp.py b/taxonomy/dhcp.py
index 0354ebd..26ecb82 100644
--- a/taxonomy/dhcp.py
+++ b/taxonomy/dhcp.py
@@ -59,6 +59,7 @@
     '1,3,6,15,119,95,252,44,46,47': ['ipodtouch1'],
 
     '252,3,42,15,6,1,12': ['lgtv', 'tizen'],
+    '252,3,42,6,1,12': ['tizen'],
 
     '1,3,6,15,119,95,252,44,46,101': ['macos'],
     '1,3,6,15,119,95,252,44,46': ['macos'],
@@ -74,8 +75,9 @@
 
     '1,28,2,3,15,6,12': ['tivo'],
 
-    '1,3,6,12,15,28,42': ['viziotv', 'wemo', 'directv'],
+    '1,3,6,12,15,28,42': ['viziotv', 'wemo', 'directv', 'samsungtv'],
     '1,3,6,12,15,28,40,41,42': ['viziotv', 'kindle'],
+    '1,3,6,12,15,17,23,28,29,31,33,40,41,42': ['viziotv'],
 
     '1,3,6,15,28,33': ['wii'],
     '1,3,6,15': ['wii', 'xbox'],
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index 00be466..85a4522 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -51,6 +51,8 @@
     'd8:50:e6': ['asus'],
     'f8:32:e4': ['asus'],
 
+    '58:67:1a': ['barnes&noble'],
+
     '30:8c:fb': ['dropcam'],
 
     '00:1a:11': ['google'],
@@ -245,6 +247,7 @@
     'b8:57:d8': ['samsung'],
     'b8:5a:73': ['samsung'],
     'b8:5e:7b': ['samsung'],
+    'bc:14:85': ['samsung'],
     'bc:20:a4': ['samsung'],
     'bc:72:b1': ['samsung'],
     'bc:8c:cd': ['samsung'],
@@ -279,8 +282,12 @@
     '58:48:22': ['sony'],
     'b4:52:7e': ['sony'],
 
+    '10:08:c1': ['toshiba'],
+
     'a4:8d:3b': ['vizio'],
 
+    'b4:79:a7': ['wink'],
+
     '00:24:e4': ['withings'],
 
     '64:cc:2e': ['xiaomi'],
diff --git a/taxonomy/pcaptest.py b/taxonomy/pcaptest.py
index 2b864e1..2b5d90f 100644
--- a/taxonomy/pcaptest.py
+++ b/taxonomy/pcaptest.py
@@ -45,13 +45,22 @@
   ('', './testdata/pcaps/Samsung Exhibit 2.4GHz.pcap'),
   ('', './testdata/pcaps/Samsung Fascinate 2.4GHz.pcap'),
   ('', './testdata/pcaps/Samsung Galaxy Tab 2 2.4GHz.pcap'),
+  ('', './testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap'),
   ('', './testdata/pcaps/Samsung Infuse 5GHz.pcap'),
   ('', './testdata/pcaps/Samsung Vibrant 2.4GHz.pcap'),
+  ('', './testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap'),
 
   # Names where the identified species doesn't exactly match the filename,
   # usually because multiple devices are too similar to distinguish. We name
   # the file for the specific device which was captured, and add an entry
   # here for the best identification which we can manage.
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap'),
+  ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap'),
   ('iPad 1st or 2nd gen', './testdata/pcaps/iPad 1st gen 5GHz.pcap'),
   ('iPad 1st or 2nd gen', './testdata/pcaps/iPad 2nd gen 5GHz.pcap'),
   ('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad (4th gen) 5GHz.pcap'),
diff --git a/taxonomy/testdata/dhcp.leases b/taxonomy/testdata/dhcp.leases
index 9bdc396..ab83a43 100644
--- a/taxonomy/testdata/dhcp.leases
+++ b/taxonomy/testdata/dhcp.leases
@@ -56,3 +56,16 @@
 1432237016 a4:8d:3b:00:00:00 192.168.42.45 VizioSmartTV
 1432237016 00:11:d9:00:00:00 192.168.42.46 TiVoBOLT
 1432237016 ac:3a:7a:00:00:00 192.168.42.47 Roku3-4230
+1432237016 d4:63:fe:00:00:00 192.168.42.48 LGSmartTV
+1432237016 bc:14:85:00:00:00 192.168.42.49 SamsungTizenTV
+1432237016 78:bd:bc:00:00:00 192.168.42.50 SamsungTizenTV
+1432237016 54:88:0e:00:00:00 192.168.42.51 SamsungLED75TV
+1432237016 bc:30:7d:00:00:00 192.168.42.52 PanasonicTV
+1432237016 60:12:8b:00:00:00 192.168.42.53 CanonPixma
+1432237016 88:87:17:00:00:00 192.168.42.54 CanonPixma
+1432237016 cc:95:d7:00:00:00 192.168.42.55 VizioTV
+1432237016 c0:f2:fb:00:00:00 192.168.42.56 iPaadMini3
+1432237016 04:52:f3:00:00:00 192.168.42.57 iPaadMini4
+1432237016 a4:d1:d2:00:00:00 192.168.42.58 iPaadOldiOS
+1432237016 70:48:0f:00:00:00 192.168.42.59 iPadPro12_9
+1432237016 6c:c2:17:00:00:00 192.168.42.60 HPPrinter
diff --git a/taxonomy/testdata/dhcp.signatures b/taxonomy/testdata/dhcp.signatures
index e9ef842..1fcfa61 100644
--- a/taxonomy/testdata/dhcp.signatures
+++ b/taxonomy/testdata/dhcp.signatures
@@ -48,3 +48,16 @@
 a4:8d:3b:00:00:00 1,3,6,12,15,28,42
 00:11:d9:00:00:00 1,28,2,3,15,6,12
 ac:3a:7a:00:00:00 1,3,6,15,12
+d4:63:fe:00:00:00 252,3,42,15,6,1,12
+bc:14:85:00:00:00 252,3,42,6,1,12
+78:bd:bc:00:00:00 252,3,42,6,1,12
+54:88:0e:00:00:00 1,3,6,12,15,28,42
+bc:30:7d:00:00:00 58,59,6,15,51,54,1,3
+60:12:8b:00:00:00 1,3,6,15,44,47
+88:87:17:00:00:00 1,3,6,15,44,47
+cc:95:d7:00:00:00 1,3,6,12,15,17,23,28,29,31,33,40,41,42
+c0:f2:fb:00:00:00 1,3,6,15,119,252
+04:52:f3:00:00:00 1,3,6,15,119,252
+a4:d1:d2:00:00:00 1,3,6,15,119,252
+70:48:0f:00:00:00 1,3,6,15,119,252
+6c:c2:17:00:00:00 6,3,1,15,66,67,13,44,12,81,252
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap
new file mode 100644
index 0000000..7e26437
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz 4th gen B00E.pcap b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz 4th gen B00E.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap" "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap"
new file mode 100644
index 0000000..d768c82
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap"
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap" "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap"
new file mode 100644
index 0000000..c595fbb
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap"
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Paperwhite B024.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Paperwhite B024.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap
new file mode 100644
index 0000000..677d9e8
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap
new file mode 100644
index 0000000..75cb5d0
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B013.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B013.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B054.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B054.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap b/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap
new file mode 100644
index 0000000..eee871c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap
new file mode 100644
index 0000000..b9fcd4b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap
new file mode 100644
index 0000000..514eac4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap
new file mode 100644
index 0000000..a361567
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap
new file mode 100644
index 0000000..12b33a1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap b/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap
new file mode 100644
index 0000000..c62092f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..e25b5cb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..e6cf5bd
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..a0eb75d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..f576a6c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap
new file mode 100644
index 0000000..25d831a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap
new file mode 100644
index 0000000..6a64e36
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap
new file mode 100644
index 0000000..023077a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap
new file mode 100644
index 0000000..562e8a2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap
new file mode 100644
index 0000000..9a28122
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap
new file mode 100644
index 0000000..4a958c2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap
new file mode 100644
index 0000000..9c0ea3d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap
new file mode 100644
index 0000000..c84bb4e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap
new file mode 100644
index 0000000..17b1931
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap
new file mode 100644
index 0000000..bae308d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap
new file mode 100644
index 0000000..eaaae3e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN60F6300AF.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN60F6300AF.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap
new file mode 100644
index 0000000..d3556c2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap
new file mode 100644
index 0000000..19675e7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap
new file mode 100644
index 0000000..6ea8b14
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap
new file mode 100644
index 0000000..1f5a8e5
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap
new file mode 100644
index 0000000..f2a4ab1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap
new file mode 100644
index 0000000..3cbf5eb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap
new file mode 100644
index 0000000..8d7cd18
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap
new file mode 100644
index 0000000..2f941b5
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN60F6300AF.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Samsung Smart TV 5GHz.pcap
rename to taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN60F6300AF.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..0284c95
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap
new file mode 100644
index 0000000..a3fcae1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..976e3bb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap
new file mode 100644
index 0000000..7a91a9b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..017f6e1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..423fb96
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..45fc9d9
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..925cda6
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..2dfaf35
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap
new file mode 100644
index 0000000..7c961dd
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..aa4ec4d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap
new file mode 100644
index 0000000..3068a62
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap b/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap
new file mode 100644
index 0000000..9dee0c4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap
new file mode 100644
index 0000000..a5fc0e7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap
new file mode 100644
index 0000000..926c99e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap
new file mode 100644
index 0000000..625f35a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap
new file mode 100644
index 0000000..7da522f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap
new file mode 100644
index 0000000..62f1748
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap
new file mode 100644
index 0000000..513b9b6
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap
new file mode 100644
index 0000000..e662f82
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap
new file mode 100644
index 0000000..d278248
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap
new file mode 100644
index 0000000..b1784dc
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..bc78522
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap
new file mode 100644
index 0000000..1d98058
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..1bf9860
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap
new file mode 100644
index 0000000..247e5da
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..afd7217
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap
new file mode 100644
index 0000000..20c9e07
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap
new file mode 100644
index 0000000..03be132
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap
new file mode 100644
index 0000000..0398616
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap
new file mode 100644
index 0000000..5c2b5b7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap
new file mode 100644
index 0000000..7ad8491
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap
new file mode 100644
index 0000000..29b07ca
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap
new file mode 100644
index 0000000..9801c46
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index aa9a0cb..3445f09 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -74,7 +74,7 @@
     'wifi4|probe:0,1,50|assoc:0,1,50,221(0050f2,2)|oui:amazon':
         ('Amazon Kindle', 'Keyboard 3', '2.4GHz'),
     'wifi4|probe:0,1,50,45,htcap:002c,htagg:01,htmcs:000000ff|assoc:0,1,50,45,48,221(0050f2,2),htcap:002c,htagg:01,htmcs:000000ff|oui:amazon':
-        ('Amazon Kindle', 'Voyage, Paperwhite, or 4th gen', '2.4GHz'),
+        ('Amazon Kindle', '', '2.4GHz'),
 
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:1130,htagg:18,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),htcap:1130,htagg:18,htmcs:000000ff|oui:amazon':
         ('Amazon Kindle', 'Fire 7" (2011 edition)', '2.4GHz'),
@@ -121,6 +121,9 @@
     'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff|os:ios':
         ('Apple Watch', '', '2.4GHz'),
 
+    'wifi4|probe:0,1,50,45,htcap:1030,htagg:18,htmcs:000000ff|assoc:0,1,50,46,48,45,221(0050f2,2),htcap:1030,htagg:18,htmcs:000000ff|oui:barnes&noble':
+        ('Barnes & Noble Nook', 'Color', '2.4GHz'),
+
     'wifi4|probe:0,1,50,221(0050f2,4)|assoc:0,1,50,45,221(0050f2,2),48,htcap:000c,htagg:1b,htmcs:000000ff|os:wemo':
         ('Belkin WeMo', 'Switch', '2.4GHz'),
 
@@ -136,10 +139,17 @@
     'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:112c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:112c,htagg:19,htmcs:000000ff|os:brotherprinter':
         ('Brother Printer', '', '2.4GHz'),
 
+    # MX410, probably others
     'wifi4|probe:0,1,3,45,50,htcap:007e,htagg:00,htmcs:000000ff|assoc:0,1,45,48,50,221(0050f2,2),htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
         ('Canon Printer', '', '2.4GHz'),
+    # MX492, probably others
     'wifi4|probe:0,1,3,45,50,htcap:007e,htagg:00,htmcs:000000ff|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
         ('Canon Printer', '', '2.4GHz'),
+    # MX492 has been seen to send these massive Probe packets. Likely other models, too.
+    'wifi4|probe:64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,127,11,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,extcap:|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
+        ('Canon Printer', '', '2.4GHz'),
+    'wifi4|probe:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
+        ('Canon Printer', '', '2.4GHz'),
 
     'wifi4|probe:0,1,45,191,htcap:11e2,htagg:17,htmcs:0000ffff,vhtcap:038071a0,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,48,45,127,191,221(0050f2,2),htcap:11e6,htagg:17,htmcs:0000ffff,vhtcap:038001a0,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000000000000040|os:chromeos':
         ('Chromebook', 'Pixel 2', '5GHz'),
@@ -268,6 +278,8 @@
 
     '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':
+        ('iPad', '1st or 2nd gen', '5GHz'),
     'wifi4|probe:0,1,45,221(001018,2),221(00904c,51),htcap:0800,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0800,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
         ('iPad', '1st or 2nd gen', '5GHz'),
     'wifi4|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:180c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:180c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
@@ -334,6 +346,20 @@
     'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1603|os:ios':
         ('iPad Mini', '2nd gen', '2.4GHz'),
 
+    'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e001|os:ios':
+        ('iPad Mini', '3rd gen', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e001|os:ios':
+        ('iPad Mini', '3rd gen', '5GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1201|os:ios':
+        ('iPad Mini', '3rd gen', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1201|os:ios':
+        ('iPad Mini', '3rd gen', '2.4GHz'),
+
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
+        ('iPad Mini', '4th gen', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
+        ('iPad Mini', '4th gen', '5GHz'),
+
     'wifi4|probe:0,1,3,50|assoc:0,1,48,50|os:ios':
         ('iPhone 2', '', '2.4GHz'),
 
@@ -501,8 +527,16 @@
     'wifi4|probe:0,1,3,45,221(0050f2,8),221(0050f2,4),221(506f9a,9),htcap:016e,htagg:03,htmcs:000000ff,wps:LG_V400|assoc:0,1,33,36,48,70,45,221(0050f2,2),127,htcap:016e,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a0200000000':
         ('LG Pad', 'v400', '5GHz'),
 
+    'wifi4|probe:0,1,45,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:lgtv':
+        ('LG Smart TV', '', '5GHz'),
+    'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,wps:_|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:lgtv':
+        ('LG Smart TV', '', '5GHz'),
+    'wifi4|probe:0,1,50,3,45,127,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000000000000040,wps:_|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:lgtv':
+        ('LG Smart TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,127,221(001018,2),221(00904c,51),htcap:11ac,htagg:16,htmcs:0000ffff,extcap:0000000000000040|assoc:0,1,33,36,48,50,45,127,221(001018,2),221(0050f2,2),htcap:11ac,htagg:16,htmcs:0000ffff,txpow:140a,extcap:0000000000000040|os:lgtv':
         ('LG Smart TV', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,wps:_|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:lgtv':
+        ('LG Smart TV', '', '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:LGLS660|assoc:0,1,50,48,45,221(0050f2,2),htcap:012c,htagg:03,htmcs:000000ff':
         ('LG Tribute', '', '2.4GHz'),
@@ -741,6 +775,10 @@
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a0200000000|oui:oneplus':
         ('Oneplus', 'X', '2.4GHz'),
 
+    'wifi4|probe:0,1,45,221(0050f2,4),htcap:11ee,htagg:02,htmcs:0000ffff,wps:WPS_STA|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0b00,extcap:01|os:panasonictv':
+        ('Panasonic TV', '', '5GHz'),
+    'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ac,htagg:02,htmcs:0000ffff,wps:WPS_STA|assoc:0,1,50,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:panasonictv':
+        ('Panasonic TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,48|assoc:0,1,33,36,50,221(0050f2,2),45,221(00037f,1),221(00037f,4),48,htcap:1004,htagg:1b,htmcs:0000ffff,txpow:0f0f|os:panasonictv':
         ('Panasonic TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ad,htagg:02,htmcs:0000ffff,wps:WPS_SUPPLICANT_STATION|assoc:0,1,50,45,48,221(0050f2,2),htcap:01ad,htagg:02,htmcs:0000ffff|os:panasonictv':
@@ -1005,12 +1043,30 @@
 
     'wifi4|probe:0,1,45,htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,45,127,33,36,48,221(0050f2,2),htcap:11ee,htagg:02,htmcs:0000ffff,txpow:1100,extcap:01|os:samsungtv':
         ('Samsung Smart TV', '', '5GHz'),
+    # UN55JS9000, probably more
+    'wifi4|probe:0,1,45,127,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000000000000040|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009,extcap:0000000000000040|os:tizen':
+        ('Samsung Smart TV', '', '5GHz'),
+    # LED75
+    'wifi4|probe:0,1,45,221(002d25,32),htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,33,36,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0e00,extcap:01|os:samsungtv':
+        ('Samsung Smart TV', '', '5GHz'),
+    # UN46ES7100F
+    'wifi4|probe:0,1,45,221(002d25,32),htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0e00,extcap:01|os:samsungtv':
+        ('Samsung Smart TV', '', '5GHz'),
+    # UN40JU6500
+    'wifi4|probe:0,1,45,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:tizen':
+        ('Samsung Smart TV', '', '5GHz'),
     'wifi4|probe:0,1,50,45,htcap:01ac,htagg:02,htmcs:0000ffff|assoc:0,1,50,45,127,48,221(0050f2,2),htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:samsungtv':
         ('Samsung Smart TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,45,221(002d25,32),htcap:01ac,htagg:02,htmcs:0000ffff|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:samsungtv':
         ('Samsung Smart TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,45,htcap:0120,htagg:02,htmcs:000000ff|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:0120,htagg:02,htmcs:000000ff,extcap:01|os:samsungtv':
         ('Samsung Smart TV', '', '2.4GHz'),
+    # UN40JU6500, probably more
+    'wifi4|probe:0,1,50,3,45,127,221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000000000000040|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:tizen':
+        ('Samsung Smart TV', '', '2.4GHz'),
+    # UN40JU6500, probably more
+    'wifi4|probe:0,1,50,3,45,221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:tizen':
+        ('Samsung Smart TV', '', '2.4GHz'),
 
     'wifi4|probe:0,1,50,3,45,htcap:11ef,htagg:1b,htmcs:0000ffff|assoc:0,1,50,48,45,221(0050f2,2),htcap:11ef,htagg:1b,htmcs:0000ffff|oui:sling':
         ('Slingbox', '500', '2.4GHz'),
@@ -1021,6 +1077,12 @@
         ('Sony Bravia TV', '2015 model', '5GHz'),
     'wifi4|probe:0,1,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_2015|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:01ef,htagg:13,htmcs:0000ffff,extcap:00000a02':
         ('Sony Bravia TV', '2015 model', '5GHz'),
+    'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:11ef,htagg:13,htmcs:0000ffff,vhtcap:31c139b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,wps:BRAVIA_4K_2015|assoc:0,1,45,191,127,221(000c43,6),221(0050f2,2),48,127,htcap:006f,htagg:13,htmcs:0000ffff,vhtcap:31c001b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a02':
+        ('Sony Bravia TV', '2015 model', '5GHz'),
+    'wifi4|probe:0,1,45,221(0050f2,4),htcap:11ee,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0c00,extcap:01':
+        ('Sony Bravia TV', '', '5GHz'),
+    'wifi4|probe:0,1,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_4K_2015|assoc:0,1,45,191,127,221(000c43,6),221(0050f2,2),48,127,htcap:006f,htagg:13,htmcs:0000ffff,vhtcap:31c001b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a02':
+        ('Sony Bravia TV', '2015 model', '5GHz'),
     'wifi4|probe:0,1,50,45,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:01ad,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,50,45,127,48,221(0050f2,2),htcap:01ad,htagg:02,htmcs:0000ffff,extcap:01':
         ('Sony Bravia TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ac,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01':
@@ -1033,6 +1095,8 @@
         ('Sony Bravia TV', '2015 model', '2.4GHz'),
     'wifi4|probe:0,1,50,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_2015|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:01ad,htagg:13,htmcs:0000ffff,extcap:00000a02':
         ('Sony Bravia TV', '2015 model', '2.4GHz'),
+    'wifi4|probe:0,1,50,45,127,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:11ef,htagg:13,htmcs:0000ffff,extcap:00,wps:BRAVIA_4K_2015|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:008c,htagg:13,htmcs:0000ffff,extcap:00000a02':
+        ('Sony Bravia TV', '2015 model', '2.4GHz'),
 
     'wifi4|probe:0,1,3,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31800120,vhtrxmcs:0000fffc,vhttxmcs:0000fffc|assoc:0,1,33,36,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff|oui:sony':
         ('Sony Xperia', 'Z Ultra', '5GHz'),
@@ -1077,6 +1141,21 @@
     'wifi4|probe:0,1,50,221(001018,2)|assoc:0,1,48,50,221(001018,2)|os:tivo':
         ('TiVo', 'Series3 or Series4', '2.4GHz'),
 
+    'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:106e,htagg:13,htmcs:0000ffff,wps:Ralink_Wireless_Linux_Client|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,htcap:000e,htagg:13,htmcs:0000ffff,extcap:00|oui:toshiba':
+        ('Toshiba Smart TV', '', '5GHz'),
+    'wifi4|probe:0,1,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,htcap:000e,htagg:13,htmcs:0000ffff,extcap:00|oui:toshiba':
+        ('Toshiba Smart TV', '', '5GHz'),
+    'wifi4|probe:0,1,50,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:13,htmcs:0000ffff,extcap:01|oui:toshiba':
+        ('Toshiba Smart TV', '', '2.4GHz'),
+    'wifi4|probe:0,1,50,45,127,221(0050f2,4),htcap:106e,htagg:13,htmcs:0000ffff,extcap:00,wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:13,htmcs:0000ffff,extcap:01|oui:toshiba':
+        ('Toshiba Smart TV', '', '2.4GHz'),
+
+    # P602ui-B3
+    'wifi4|probe:0,1,50,221(0050f2,4),wps:_|assoc:0,33,36,1,48,221(0050f2,2),45,127,htcap:106e,htagg:1f,htmcs:0000ffff,txpow:150d,extcap:0000000000000000|os:viziotv':
+        ('Vizio Smart TV', '', '5GHz'),
+    # P602ui-B3
+    'wifi4|probe:0,1,50|assoc:0,33,36,1,48,221(0050f2,2),45,127,htcap:106e,htagg:1f,htmcs:0000ffff,txpow:150d,extcap:0000000000000000|os:viziotv':
+        ('Vizio Smart TV', '', '5GHz'),
     'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f8159b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,wps:_|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f8159b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002|oui:vizio':
         ('Vizio SmartCast TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:12,htmcs:000000ff,extcap:01000000|os:viziotv':
@@ -1091,6 +1170,12 @@
         ('Vizio Smart TV', '', '2.4GHz'),
     'wifi4|probe:0,1,50,48|assoc:0,1,50,221(0050f2,2),45,51,127,48,htcap:012c,htagg:1b,htmcs:000000ff,extcap:01|os:viziotv':
         ('Vizio Smart TV', '', '2.4GHz'),
+    # P602ui-B3
+    'wifi4|probe:0,1,50,221(0050f2,4),wps:_|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:122c,htagg:1f,htmcs:0000ffff,extcap:0000000000000000|os:viziotv':
+        ('Vizio Smart TV', '', '2.4GHz'),
+    # P602ui-B3
+    'wifi4|probe:0,1,50|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:122c,htagg:1f,htmcs:0000ffff,extcap:0000000000000000|os:viziotv':
+        ('Vizio Smart TV', '', '2.4GHz'),
 
     'wifi4|probe:0,1,3,45,221(0050f2,8),htcap:016e,htagg:03,htmcs:000000ff|assoc:0,1,33,36,45,221(0050f2,2),htcap:016e,htagg:03,htmcs:000000ff,txpow:110d|oui:vizio':
         ('Vizio Tablet', 'XR6P', '5GHz'),
@@ -1103,6 +1188,9 @@
     'wifi4|probe:0,1,50,45,3,221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(00904c,51),221(0050f2,2),htcap:100c,htagg:19,htmcs:000000ff|os:wii':
         ('Wii-U', '', '2.4GHz'),
 
+    'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:1020,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:1020,htagg:1a,htmcs:000000ff,txpow:1009|oui:wink':
+        ('Wink Hub', '', '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|oui:withings':
         ('Withings Scale', '', '2.4GHz'),
 
@@ -1135,6 +1223,15 @@
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,33,48,70,45,221(0050f2,2),htcap:012c,htagg:03,htmcs:000000ff,txpow:170d|oui:xiaomi':
         ('Xiaomi Redmi', '3', '2.4GHz'),
 
+    'wifi4|probe:0,1,45,221(0050f2,8),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:040000000000004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,txpow:1408,extcap:040000000000004080|oui:xiaomi':
+        ('Xiaomi Mi', '5', '5GHz'),
+    'wifi4|probe:0,1,45,191,3,127,htcap:016f,htagg:df,htmcs:000000ff,vhtcap:33800132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:04000a020100004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,txpow:1408,extcap:040000000000004080|oui:xiaomi':
+        ('Xiaomi Mi', '5', '5GHz'),
+    'wifi4|probe:0,1,50,45,191,3,127,htcap:016f,htagg:df,htmcs:000000ff,vhtcap:33800132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:04000a020100004080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012d,htagg:1f,htmcs:000000ff,txpow:1408,extcap:040000000000000080|oui:xiaomi':
+        ('Xiaomi Mi', '5', '2.4GHz'),
+    'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,htcap:012d,htagg:1f,htmcs:000000ff,extcap:040000000000000080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012d,htagg:1f,htmcs:000000ff,txpow:1408,extcap:040000000000000080|oui:xiaomi':
+        ('Xiaomi Mi', '5', '2.4GHz'),
+
     'wifi4|probe:0,1,50,221(0050f2,4),221(506f9a,9),wps:Z820|assoc:0,1,50,45,48,127,221(0050f2,2),htcap:1172,htagg:03,htmcs:000000ff,extcap:01':
         ('ZTE Obsidian', '', '2.4GHz'),
 }
diff --git a/wifi/wifi.py b/wifi/wifi.py
index b0ef7f9..8797633 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -542,9 +542,20 @@
       ('hostapd_cli', '-i', interface, 'status'), no_stdout=True) == 0
 
 
-def _is_wpa_supplicant_running(interface):
+def _wpa_cli(program, interface, command):
   return utils.subprocess_quiet(
-      ('wpa_cli', '-i', interface, 'status'), no_stdout=True) == 0
+      (program, '-i', interface, command), no_stdout=True) == 0
+
+
+def _is_wpa_supplicant_running(interface):
+  return _wpa_cli('wpa_cli', interface, 'status')
+
+
+def _reconfigure_wpa_supplicant(interface):
+  if not _wpa_cli('wpa_cli', interface, 'reconfigure'):
+    return False
+
+  return _wait_for_wpa_supplicant_to_associate(interface)
 
 
 def _hostapd_debug_options():
@@ -653,6 +664,38 @@
         return None
 
 
+def _wait_for_wpa_supplicant_to_associate(interface):
+  """Wait for wpa_supplicant to associate.
+
+  If it does not associate within a certain period of time, terminate it.
+
+  Args:
+    interface: The interface on which wpa_supplicant is running.
+
+  Raises:
+    BinWifiException: if wpa_supplicant fails to associate and
+    also cannot be stopped to cleanup after the failure.
+
+  Returns:
+    Whether wpa_supplicant associated within the timeout.
+  """
+  utils.log('Waiting for wpa_supplicant to connect')
+  for _ in xrange(100):
+    if _get_wpa_state(interface) == 'COMPLETED':
+      utils.log('ok')
+      return True
+    sys.stderr.write('.')
+    time.sleep(0.1)
+
+  utils.log('wpa_supplicant did not connect.')
+  if not _stop_wpa_supplicant(interface):
+    raise utils.BinWifiException(
+        "Couldn't stop wpa_supplicant after it failed to connect.  "
+        "Consider killing it manually.")
+
+  return False
+
+
 def _start_wpa_supplicant(interface, config_filename):
   """Starts a babysat wpa_supplicant.
 
@@ -704,21 +747,7 @@
   else:
     return False
 
-  utils.log('Waiting for wpa_supplicant to connect')
-  for _ in xrange(100):
-    if _get_wpa_state(interface) == 'COMPLETED':
-      utils.log('ok')
-      return True
-    sys.stderr.write('.')
-    time.sleep(0.1)
-
-  utils.log('wpa_supplicant did not connect.')
-  if not _stop_wpa_supplicant(interface):
-    raise utils.BinWifiException(
-        "Couldn't stop wpa_supplicant after it failed to connect.  "
-        "Consider killing it manually.")
-
-  return False
+  return _wait_for_wpa_supplicant_to_associate(interface)
 
 
 def _maybe_restart_hostapd(interface, config, opt):
@@ -777,8 +806,7 @@
 def _restart_hostapd(band):
   """Restart hostapd from previous options.
 
-  Only used by _maybe_restart_wpa_supplicant, to restart hostapd after stopping
-  it.
+  Only used by _set_wpa_supplicant_config, to restart hostapd after stopping it.
 
   Args:
     band: The band on which to restart hostapd.
@@ -797,7 +825,7 @@
   _run(argv)
 
 
-def _maybe_restart_wpa_supplicant(interface, config, opt):
+def _set_wpa_supplicant_config(interface, config, opt):
   """Starts or restarts wpa_supplicant unless doing so would be a no-op.
 
   The no-op case (i.e. wpa_supplicant is already running with an equivalent
@@ -826,11 +854,12 @@
   except IOError:
     pass
 
-  if not _is_wpa_supplicant_running(interface):
+  already_running = _is_wpa_supplicant_running(interface)
+  if not already_running:
     utils.log('wpa_supplicant not running yet, starting.')
   elif current_config != config:
     # TODO(rofrankel): Consider using wpa_cli reconfigure here.
-    utils.log('wpa_supplicant config changed, restarting.')
+    utils.log('wpa_supplicant config changed, reconfiguring.')
   elif opt.force_restart:
     utils.log('Forced restart requested.')
     forced = True
@@ -838,12 +867,12 @@
     utils.log('wpa_supplicant-%s already configured and running', interface)
     return True
 
-  if not _stop_wpa_supplicant(interface):
-    raise utils.BinWifiException("Couldn't stop wpa_supplicant")
-
   if not forced:
     utils.atomic_write(tmp_config_filename, config)
 
+  # TODO(rofrankel): Consider removing all the restart hostapd stuff when
+  # b/30140131 is resolved.  hostapd seems to keep working without being
+  # restarted, at least on Camaro.
   restart_hostapd = False
   ap_interface = iw.find_interface_from_band(band, iw.INTERFACE_TYPE.ap,
                                              opt.interface_suffix)
@@ -852,13 +881,15 @@
     opt_without_persist = options.OptDict({})
     opt_without_persist.persist = False
     opt_without_persist.band = opt.band
-    # Code review: Will AP and client always have the same suffix?
     opt_without_persist.interface_suffix = opt.interface_suffix
     if not stop_ap_wifi(opt_without_persist):
       raise utils.BinWifiException(
           "Couldn't stop hostapd to start wpa_supplicant.")
 
-  if not _start_wpa_supplicant(interface, tmp_config_filename):
+  if already_running:
+    if not _reconfigure_wpa_supplicant(interface):
+      raise utils.BinWifiException('Failed to reconfigure wpa_supplicant.')
+  elif not _start_wpa_supplicant(interface, tmp_config_filename):
     raise utils.BinWifiException(
         'wpa_supplicant failed to start.  Look at wpa_supplicant logs for '
         'details.')
@@ -934,7 +965,7 @@
           ('ip', 'link', 'set', interface, 'address', mac_address))
 
   wpa_config = configs.generate_wpa_supplicant_config(opt.ssid, psk, opt)
-  if not _maybe_restart_wpa_supplicant(interface, wpa_config, opt):
+  if not _set_wpa_supplicant_config(interface, wpa_config, opt):
     return False
 
   return True