Merge pull request #24 from VorpalBlade/feature/code-cleanup

Major code cleanup:
This commit is contained in:
uunicorn 2020-08-06 22:41:07 +12:00 committed by GitHub
commit 6a7ae704e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 2205 additions and 1007 deletions

394
.style.yapf Normal file
View file

@ -0,0 +1,394 @@
[style]
# Align closing bracket with visual indentation.
align_closing_bracket_with_visual_indent=True
# Allow dictionary keys to exist on multiple lines. For example:
#
# x = {
# ('this is the first element of a tuple',
# 'this is the second element of a tuple'):
# value,
# }
allow_multiline_dictionary_keys=False
# Allow lambdas to be formatted on more than one line.
allow_multiline_lambdas=False
# Allow splitting before a default / named assignment in an argument list.
allow_split_before_default_or_named_assigns=True
# Allow splits before the dictionary value.
allow_split_before_dict_value=True
# Let spacing indicate operator precedence. For example:
#
# a = 1 * 2 + 3 / 4
# b = 1 / 2 - 3 * 4
# c = (1 + 2) * (3 - 4)
# d = (1 - 2) / (3 + 4)
# e = 1 * 2 - 3
# f = 1 + 2 + 3 + 4
#
# will be formatted as follows to indicate precedence:
#
# a = 1*2 + 3/4
# b = 1/2 - 3*4
# c = (1+2) * (3-4)
# d = (1-2) / (3+4)
# e = 1*2 - 3
# f = 1 + 2 + 3 + 4
#
arithmetic_precedence_indication=False
# Number of blank lines surrounding top-level function and class
# definitions.
blank_lines_around_top_level_definition=2
# Insert a blank line before a class-level docstring.
blank_line_before_class_docstring=False
# Insert a blank line before a module docstring.
blank_line_before_module_docstring=False
# Insert a blank line before a 'def' or 'class' immediately nested
# within another 'def' or 'class'. For example:
#
# class Foo:
# # <------ this blank line
# def method():
# ...
blank_line_before_nested_class_or_def=False
# Do not split consecutive brackets. Only relevant when
# dedent_closing_brackets is set. For example:
#
# call_func_that_takes_a_dict(
# {
# 'key1': 'value1',
# 'key2': 'value2',
# }
# )
#
# would reformat to:
#
# call_func_that_takes_a_dict({
# 'key1': 'value1',
# 'key2': 'value2',
# })
coalesce_brackets=False
# The column limit.
column_limit=100
# The style for continuation alignment. Possible values are:
#
# - SPACE: Use spaces for continuation alignment. This is default behavior.
# - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
# (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or
# CONTINUATION_INDENT_WIDTH spaces) for continuation alignment.
# - VALIGN-RIGHT: Vertically align continuation lines to multiple of
# INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if
# cannot vertically align continuation lines with indent characters.
continuation_align_style=SPACE
# Indent width used for line continuations.
continuation_indent_width=4
# Put closing brackets on a separate line, dedented, if the bracketed
# expression can't fit in a single line. Applies to all kinds of brackets,
# including function definitions and calls. For example:
#
# config = {
# 'key1': 'value1',
# 'key2': 'value2',
# } # <--- this bracket is dedented and on a separate line
#
# time_series = self.remote_client.query_entity_counters(
# entity='dev3246.region1',
# key='dns.query_latency_tcp',
# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
# start_ts=now()-timedelta(days=3),
# end_ts=now(),
# ) # <--- this bracket is dedented and on a separate line
dedent_closing_brackets=False
# Disable the heuristic which places each list element on a separate line
# if the list is comma-terminated.
disable_ending_comma_heuristic=False
# Place each dictionary entry onto its own line.
each_dict_entry_on_separate_line=True
# Require multiline dictionary even if it would normally fit on one line.
# For example:
#
# config = {
# 'key1': 'value1'
# }
force_multiline_dict=False
# The regex for an i18n comment. The presence of this comment stops
# reformatting of that line, because the comments are required to be
# next to the string they translate.
i18n_comment=
# The i18n function call names. The presence of this function stops
# reformattting on that line, because the string it has cannot be moved
# away from the i18n comment.
i18n_function_call=
# Indent blank lines.
indent_blank_lines=False
# Put closing brackets on a separate line, indented, if the bracketed
# expression can't fit in a single line. Applies to all kinds of brackets,
# including function definitions and calls. For example:
#
# config = {
# 'key1': 'value1',
# 'key2': 'value2',
# } # <--- this bracket is indented and on a separate line
#
# time_series = self.remote_client.query_entity_counters(
# entity='dev3246.region1',
# key='dns.query_latency_tcp',
# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
# start_ts=now()-timedelta(days=3),
# end_ts=now(),
# ) # <--- this bracket is indented and on a separate line
indent_closing_brackets=False
# Indent the dictionary value if it cannot fit on the same line as the
# dictionary key. For example:
#
# config = {
# 'key1':
# 'value1',
# 'key2': value1 +
# value2,
# }
indent_dictionary_value=False
# The number of columns to use for indentation.
indent_width=4
# Join short lines into one line. E.g., single line 'if' statements.
join_multiple_lines=True
# Do not include spaces around selected binary operators. For example:
#
# 1 + 2 * 3 - 4 / 5
#
# will be formatted as follows when configured with "*,/":
#
# 1 + 2*3 - 4/5
no_spaces_around_selected_binary_operators=
# Use spaces around default or named assigns.
spaces_around_default_or_named_assign=False
# Adds a space after the opening '{' and before the ending '}' dict delimiters.
#
# {1: 2}
#
# will be formatted as:
#
# { 1: 2 }
spaces_around_dict_delimiters=False
# Adds a space after the opening '[' and before the ending ']' list delimiters.
#
# [1, 2]
#
# will be formatted as:
#
# [ 1, 2 ]
spaces_around_list_delimiters=False
# Use spaces around the power operator.
spaces_around_power_operator=False
# Use spaces around the subscript / slice operator. For example:
#
# my_list[1 : 10 : 2]
spaces_around_subscript_colon=False
# Adds a space after the opening '(' and before the ending ')' tuple delimiters.
#
# (1, 2, 3)
#
# will be formatted as:
#
# ( 1, 2, 3 )
spaces_around_tuple_delimiters=False
# The number of spaces required before a trailing comment.
# This can be a single value (representing the number of spaces
# before each trailing comment) or list of values (representing
# alignment column values; trailing comments within a block will
# be aligned to the first column value that is greater than the maximum
# line length within the block). For example:
#
# With spaces_before_comment=5:
#
# 1 + 1 # Adding values
#
# will be formatted as:
#
# 1 + 1 # Adding values <-- 5 spaces between the end of the statement and comment
#
# With spaces_before_comment=15, 20:
#
# 1 + 1 # Adding values
# two + two # More adding
#
# longer_statement # This is a longer statement
# short # This is a shorter statement
#
# a_very_long_statement_that_extends_beyond_the_final_column # Comment
# short # This is a shorter statement
#
# will be formatted as:
#
# 1 + 1 # Adding values <-- end of line comments in block aligned to col 15
# two + two # More adding
#
# longer_statement # This is a longer statement <-- end of line comments in block aligned to col 20
# short # This is a shorter statement
#
# a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length
# short # This is a shorter statement
#
spaces_before_comment=2
# Insert a space between the ending comma and closing bracket of a list,
# etc.
space_between_ending_comma_and_closing_bracket=True
# Use spaces inside brackets, braces, and parentheses. For example:
#
# method_call( 1 )
# my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ]
# my_set = { 1, 2, 3 }
space_inside_brackets=False
# Split before arguments
split_all_comma_separated_values=False
# Split before arguments, but do not split all subexpressions recursively
# (unless needed).
split_all_top_level_comma_separated_values=False
# Split before arguments if the argument list is terminated by a
# comma.
split_arguments_when_comma_terminated=False
# Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@'
# rather than after.
split_before_arithmetic_operator=False
# Set to True to prefer splitting before '&', '|' or '^' rather than
# after.
split_before_bitwise_operator=True
# Split before the closing bracket if a list or dict literal doesn't fit on
# a single line.
split_before_closing_bracket=True
# Split before a dictionary or set generator (comp_for). For example, note
# the split before the 'for':
#
# foo = {
# variable: 'Hello world, have a nice day!'
# for variable in bar if variable != 42
# }
split_before_dict_set_generator=True
# Split before the '.' if we need to split a longer expression:
#
# foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
#
# would reformat to something like:
#
# foo = ('This is a really long string: {}, {}, {}, {}'
# .format(a, b, c, d))
split_before_dot=False
# Split after the opening paren which surrounds an expression if it doesn't
# fit on a single line.
split_before_expression_after_opening_paren=False
# If an argument / parameter list is going to be split, then split before
# the first argument.
split_before_first_argument=False
# Set to True to prefer splitting before 'and' or 'or' rather than
# after.
split_before_logical_operator=True
# Split named assignments onto individual lines.
split_before_named_assigns=True
# Set to True to split list comprehensions and generators that have
# non-trivial expressions and multiple clauses before each of these
# clauses. For example:
#
# result = [
# a_long_var + 100 for a_long_var in xrange(1000)
# if a_long_var % 10]
#
# would reformat to something like:
#
# result = [
# a_long_var + 100
# for a_long_var in xrange(1000)
# if a_long_var % 10]
split_complex_comprehension=False
# The penalty for splitting right after the opening bracket.
split_penalty_after_opening_bracket=300
# The penalty for splitting the line after a unary operator.
split_penalty_after_unary_operator=10000
# The penalty of splitting the line around the '+', '-', '*', '/', '//',
# ``%``, and '@' operators.
split_penalty_arithmetic_operator=300
# The penalty for splitting right before an if expression.
split_penalty_before_if_expr=0
# The penalty of splitting the line around the '&', '|', and '^'
# operators.
split_penalty_bitwise_operator=300
# The penalty for splitting a list comprehension or generator
# expression.
split_penalty_comprehension=80
# The penalty for characters over the column limit.
split_penalty_excess_character=7000
# The penalty incurred by adding a line split to the unwrapped line. The
# more line splits added the higher the penalty.
split_penalty_for_added_line_split=30
# The penalty of splitting a list of "import as" names. For example:
#
# from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
# long_argument_2,
# long_argument_3)
#
# would reformat to something like:
#
# from a_very_long_or_indented_module_name_yada_yad import (
# long_argument_1, long_argument_2, long_argument_3)
split_penalty_import_names=0
# The penalty of splitting the line around the 'and' and 'or'
# operators.
split_penalty_logical_operator=300
# Use the Tab character for indentation.
use_tabs=False

View file

@ -1,15 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
from binascii import unhexlify from binascii import unhexlify
from enum import Enum
from validitysensor.flash import read_flash
from validitysensor.sensor import *
from validitysensor import init
from time import sleep from time import sleep
from usb import core as usb_core
from validitysensor import init
from validitysensor.sensor import glow_start_scan, glow_end_scan
from validitysensor.tls import tls
from validitysensor.util import assert_status
if __name__ == "__main__": if __name__ == "__main__":
if os.geteuid() != 0: if os.geteuid() != 0:
@ -23,10 +21,9 @@ if __name__ == "__main__":
glow_end_scan() glow_end_scan()
sleep(0.05) sleep(0.05)
led_script = unhexlify( led_script = unhexlify('39ff100000ff03000001ff002000000000ffff0000ffff0000ff03000001ff00'
'39ff100000ff03000001ff002000000000ffff0000ffff0000ff03000001ff00' \ '200000000000000000ffff0000ff03000001ff002000000000ffff0000000000'
'200000000000000000ffff0000ff03000001ff002000000000ffff0000000000' \ '0000000000000000000000000000000000000000000000000000000000000000'
'0000000000000000000000000000000000000000000000000000000000000000' \ '0000000000000000000000000000000000000000000000000000000000')
'0000000000000000000000000000000000000000000000000000000000')
assert_status(tls.app(led_script)) assert_status(tls.app(led_script))

View file

@ -20,17 +20,17 @@
import argparse import argparse
import os import os
import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import urllib.request import urllib.request
import shutil from enum import Enum
from enum import Enum, auto
from time import sleep
from usb import core as usb_core from usb import core as usb_core
python_validity_data='/usr/share/python-validity/' python_validity_data = '/usr/share/python-validity/'
# FIXME: supported usb ids are duplicated in usb.py # FIXME: supported usb ids are duplicated in usb.py
class VFS(Enum): class VFS(Enum):
@ -38,6 +38,7 @@ class VFS(Enum):
DEV_97 = (0x138a, 0x0097) DEV_97 = (0x138a, 0x0097)
DEV_9a = (0x06cb, 0x009a) DEV_9a = (0x06cb, 0x009a)
DEFAULT_URIS = { DEFAULT_URIS = {
VFS.DEV_90: { VFS.DEV_90: {
'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1cgn08w.exe', 'driver': 'https://download.lenovo.com/pccbbs/mobiles/n1cgn08w.exe',
@ -76,15 +77,12 @@ def download_and_extract_fw(dev_type, fwdir, fwuri=None):
with open(fwarchive, 'wb') as out_file: with open(fwarchive, 'wb') as out_file:
out_file.write(response.read()) out_file.write(response.read())
subprocess.check_call(['innoextract', subprocess.check_call([
'--output-dir', fwdir, 'innoextract', '--output-dir', fwdir, '--include', fwname, '--collisions', 'overwrite',
'--include', fwname,
'--collisions', 'overwrite',
fwarchive fwarchive
]) ])
fwpath = subprocess.check_output([ fwpath = subprocess.check_output(['find', fwdir, '-name', fwname]).decode('utf-8').strip()
'find', fwdir, '-name', fwname]).decode('utf-8').strip()
print('Found firmware at {}'.format(fwpath)) print('Found firmware at {}'.format(fwpath))
if not fwpath: if not fwpath:
@ -92,6 +90,7 @@ def download_and_extract_fw(dev_type, fwdir, fwuri=None):
return fwpath return fwpath
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--driver-uri') parser.add_argument('--driver-uri')
@ -111,8 +110,7 @@ if __name__ == "__main__":
raise Exception('No supported validity device found') raise Exception('No supported validity device found')
try: try:
subprocess.check_call(['innoextract', '--version'], subprocess.check_call(['innoextract', '--version'], stdout=subprocess.DEVNULL)
stdout=subprocess.DEVNULL)
except Exception as e: except Exception as e:
print('Impossible to run innoextract: {}'.format(e)) print('Impossible to run innoextract: {}'.format(e))
sys.exit(1) sys.exit(1)

View file

@ -1,6 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import logging
import logging.handlers
import os import os
import pwd import pwd
import re import re
@ -16,21 +18,18 @@ import dbus
import dbus.mainloop.glib import dbus.mainloop.glib
import dbus.service import dbus.service
import yaml import yaml
from gi.repository import GLib
from usb import core as usb_core from usb import core as usb_core
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
from gi.repository import GLib
import logging
import logging.handlers
from validitysensor import init from validitysensor import init
from validitysensor.tls import tls
from validitysensor.usb import usb
from validitysensor.sid import sid_from_string
from validitysensor.db import subtype_to_string, db, SidIdentity, User from validitysensor.db import subtype_to_string, db, SidIdentity, User
from validitysensor.sensor import sensor, RebootException from validitysensor.sensor import sensor, RebootException
from validitysensor.sid import sid_from_string
from validitysensor.tls import tls
from validitysensor.usb import usb
from validitysensor.winbio_constants import finger_ids from validitysensor.winbio_constants import finger_ids
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
GLib.threads_init() GLib.threads_init()
INTERFACE_NAME = 'io.github.uunicorn.Fprint.Device' INTERFACE_NAME = 'io.github.uunicorn.Fprint.Device'
@ -64,9 +63,7 @@ class Device(dbus.service.Object):
"""Resolve user name to database object""" """Resolve user name to database object"""
return db.lookup_user(self.user2identity(user)) return db.lookup_user(self.user2identity(user))
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature="s", out_signature="as")
in_signature="s",
out_signature="as")
def ListEnrolledFingers(self, user): def ListEnrolledFingers(self, user):
try: try:
logging.debug('In ListEnrolledFingers %s' % user) logging.debug('In ListEnrolledFingers %s' % user)
@ -82,9 +79,7 @@ class Device(dbus.service.Object):
except Exception as e: except Exception as e:
raise e raise e
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s', out_signature='')
in_signature='s',
out_signature='')
def DeleteEnrolledFingers(self, user): def DeleteEnrolledFingers(self, user):
logging.debug('In DeleteEnrolledFingers %s' % user) logging.debug('In DeleteEnrolledFingers %s' % user)
usr = self.user2record(user) usr = self.user2record(user)
@ -94,9 +89,7 @@ class Device(dbus.service.Object):
db.del_record(usr.dbid) db.del_record(usr.dbid)
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss', out_signature='')
in_signature='ss',
out_signature='')
def VerifyStart(self, user, finger): def VerifyStart(self, user, finger):
logging.debug('In VerifyStart for %s, %s' % (user, finger)) logging.debug('In VerifyStart for %s, %s' % (user, finger))
@ -130,15 +123,11 @@ class Device(dbus.service.Object):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='', out_signature='')
in_signature='',
out_signature='')
def Cancel(self): def Cancel(self):
sensor.cancel() sensor.cancel()
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss', out_signature='')
in_signature='ss',
out_signature='')
def EnrollStart(self, user, finger_name): def EnrollStart(self, user, finger_name):
logging.debug('In EnrollStart %s for %s' % (finger_name, user)) logging.debug('In EnrollStart %s for %s' % (finger_name, user))
@ -169,11 +158,9 @@ class Device(dbus.service.Object):
logging.exception(e) logging.exception(e)
self.EnrollStatus('enroll-failed', True) self.EnrollStatus('enroll-failed', True)
if index == None: if index is None:
logging.exception( logging.exception('Unknown finger name passed to enroll? ' + finger_name + ' (' +
'Unknown finger name passed to enroll? ' + winbio_name + ')')
finger_name + ' (' + winbio_name + ')'
)
self.EnrollStatus('enroll-failed', True) self.EnrollStatus('enroll-failed', True)
else: else:
thread = Thread(target=run) thread = Thread(target=run)
@ -192,9 +179,7 @@ class Device(dbus.service.Object):
def EnrollStatus(self, result, done): def EnrollStatus(self, result, done):
logging.debug('EnrollStatus') logging.debug('EnrollStatus')
@dbus.service.method(dbus_interface=INTERFACE_NAME, @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s', out_signature='s')
in_signature='s',
out_signature='s')
def RunCmd(self, cmd): def RunCmd(self, cmd):
logging.debug('RunCmd') logging.debug('RunCmd')
return hexlify(tls.app(unhexlify(cmd))).decode() return hexlify(tls.app(unhexlify(cmd))).decode()
@ -228,7 +213,10 @@ def main():
parser = argparse.ArgumentParser('Open fprintd DBus service') parser = argparse.ArgumentParser('Open fprintd DBus service')
parser.add_argument('--debug', help='Enable tracing', action='store_true') parser.add_argument('--debug', help='Enable tracing', action='store_true')
parser.add_argument('--devpath', help='USB device path: usb-<busnum>-<address>') parser.add_argument('--devpath', help='USB device path: usb-<busnum>-<address>')
parser.add_argument('--configpath', default='/etc/python-validity', type=Path, help='Path to config files') parser.add_argument('--configpath',
default='/etc/python-validity',
type=Path,
help='Path to config files')
args = parser.parse_args() args = parser.parse_args()
if args.debug: if args.debug:

View file

@ -1,13 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse
import re
import dbus
import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
from gi.repository import GObject, GLib
import sys import sys
import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus() bus = dbus.SystemBus()
o = bus.get_object('net.reactivated.Fprint', '/net/reactivated/Fprint/Manager', introspect=False) o = bus.get_object('net.reactivated.Fprint', '/net/reactivated/Fprint/Manager', introspect=False)
o = o.GetDefaultDevice() o = o.GetDefaultDevice()

View file

@ -1,6 +1,5 @@
from validitysensor.usb import usb
from validitysensor.sensor import factory_reset, RebootException from validitysensor.sensor import factory_reset, RebootException
from validitysensor.usb import usb
try: try:
usb.open() usb.open()

View file

@ -1,13 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse
import re
import dbus
import dbus.mainloop.glib import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
from gi.repository import GObject, GLib
import pwd dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus() bus = dbus.SystemBus()
o = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') o = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
@ -16,10 +11,10 @@ ls = o.ListNames()
for n in ls: for n in ls:
if n[0] != ':': if n[0] != ':':
continue continue
pid = o.GetConnectionUnixProcessID(n) pid = o.GetConnectionUnixProcessID(n)
with open('/proc/%d/cmdline' % pid) as f: with open('/proc/%d/cmdline' % pid) as f:
s=f.read() s = f.read()
s=s.split('\0') s = s.split('\0')
print('%-10s %-5d %s' % (n, pid, ' '.join(s))) print('%-10s %-5d %s' % (n, pid, ' '.join(s)))

View file

@ -1,3 +1,7 @@
import code
import logging
from binascii import hexlify
from validitysensor.tls import tls from validitysensor.tls import tls
from validitysensor.usb import usb from validitysensor.usb import usb
from validitysensor.db import db from validitysensor.db import db
@ -7,11 +11,10 @@ from validitysensor.sid import *
from validitysensor.init import open as open9x from validitysensor.init import open as open9x
from threading import Condition from threading import Condition
from time import sleep from time import sleep
import logging
import code
#usb.trace_enabled = True # usb.trace_enabled = True
#tls.trace_enabled = True # tls.trace_enabled = True
def identify(): def identify():
def update_cb(e): def update_cb(e):
@ -21,6 +24,7 @@ def identify():
print('Got finger %x for user recordid %d. Hash: %s' % (subtype, usrid, hexlify(hsh).decode())) print('Got finger %x for user recordid %d. Hash: %s' % (subtype, usrid, hexlify(hsh).decode()))
def enroll(sid, finger): def enroll(sid, finger):
def update_cb(x, e): def update_cb(x, e):
if e is not None: if e is not None:
@ -31,4 +35,3 @@ def enroll(sid, finger):
recid = sensor.enroll(sid, finger, update_cb) recid = sensor.enroll(sid, finger, update_cb)
print('Created a finger record with dbid %d' % recid) print('Created a finger record with dbid %d' % recid)

View file

@ -3,26 +3,19 @@
from setuptools import setup from setuptools import setup
setup(name='python-validity', setup(name='python-validity',
version='0.9', version='0.9',
py_modules = [], py_modules=[],
packages=['validitysensor'], packages=['validitysensor'],
scripts=[ scripts=[
'bin/validity-led-dance', 'bin/validity-led-dance',
'bin/validity-sensors-firmware', 'bin/validity-sensors-firmware',
], ],
install_requires=[ install_requires=['cryptography >= 2.1.4', 'pyusb >= 1.0.0', 'pyyaml >= 3.12'],
'cryptography >= 2.1.4', data_files=[
'pyusb >= 1.0.0', ('share/dbus-1/system.d/', ['dbus_service/io.github.uunicorn.Fprint.conf']),
'pyyaml >= 3.12' ('lib/python-validity/', ['dbus_service/dbus-service']),
], ('share/python-validity/playground/', [
data_files=[ 'scripts/dbus-cmd.py', 'scripts/lsdbus.py', 'scripts/factory-reset.py',
('share/dbus-1/system.d/', ['dbus_service/io.github.uunicorn.Fprint.conf']), 'scripts/prototype.py'
('lib/python-validity/', ['dbus_service/dbus-service']), ]),
('share/python-validity/playground/', [ ])
'scripts/dbus-cmd.py',
'scripts/lsdbus.py',
'scripts/factory-reset.py',
'scripts/prototype.py'
]),
]
)

View file

@ -1,13 +1,4 @@
from enum import Enum, auto def __load_blob(blob: str) -> bytes:
class Blobs(Enum):
init_hardcoded = auto()
init_hardcoded_clean_slate = auto()
reset_blob = auto()
db_write_enable = auto()
def __load_blob(blob):
from .usb import usb from .usb import usb
if usb.usb_dev().idVendor == 0x138a: if usb.usb_dev().idVendor == 0x138a:
@ -22,6 +13,8 @@ def __load_blob(blob):
globals()[blob] = getattr(blobs, blob) globals()[blob] = getattr(blobs, blob)
return globals()[blob] return globals()[blob]
for p in dir(Blobs):
if isinstance(getattr(Blobs, p), Blobs): init_hardcoded = lambda: __load_blob('init_hardcoded')
globals()[p] = lambda bname=p: __load_blob(bname) init_hardcoded_clean_slate = lambda: __load_blob('init_hardcoded_clean_slate')
reset_blob = lambda: __load_blob('reset_blob')
db_write_enable = lambda: __load_blob('db_write_enable')

View file

@ -1,7 +1,6 @@
from .util import unhex from .util import unhex
init_hardcoded=unhex(''' init_hardcoded = unhex('''
06020000013917b3dda91383b5bcac64fa4ad35dce96570a9d2d974b80926a431f9cd46248980a263c6fcef6a82839a90b59ac590848859 06020000013917b3dda91383b5bcac64fa4ad35dce96570a9d2d974b80926a431f9cd46248980a263c6fcef6a82839a90b59ac590848859
afac817b7d53bf51cd3205c1b8f43048be8253c3bd247937c837aca8b18d3cc8ee8c8971ac4f688813cf3d8550d71496985b7ec07ff2dc7 afac817b7d53bf51cd3205c1b8f43048be8253c3bd247937c837aca8b18d3cc8ee8c8971ac4f688813cf3d8550d71496985b7ec07ff2dc7
896d330fdab263a0ee433a5c4bc910439d1c6161853feb03f5502209502e7308beb7919473cfe69f422c30502d226a4d0a34d96c8c77956 896d330fdab263a0ee433a5c4bc910439d1c6161853feb03f5502209502e7308beb7919473cfe69f422c30502d226a4d0a34d96c8c77956
@ -13,9 +12,9 @@ e024ca6b9a48332c1a69a5a3fdd24b964cf7e7c55229bb0b48a6e339eb2c42d07ec850a4ee780660
b3d5cad8d5bd7cea37e58a31307a6d50e6ae379a53f1366678c0741a3d872b8dcfefa7f63128dc8245 b3d5cad8d5bd7cea37e58a31307a6d50e6ae379a53f1366678c0741a3d872b8dcfefa7f63128dc8245
''') ''')
init_hardcoded_clean_slate=unhex('') init_hardcoded_clean_slate = unhex('')
reset_blob=unhex(''' reset_blob = unhex('''
0602000001d6123241376f110935a7ae38b614ced9952eaa38e2984842d0552d984b69ab5c1a70175513319970225d9ca25be063a7d70a2 0602000001d6123241376f110935a7ae38b614ced9952eaa38e2984842d0552d984b69ab5c1a70175513319970225d9ca25be063a7d70a2
9dff803240c099ba5386910b69c3b7ca8d0b26ec9a5684fe92af9c6d8068ace0954eb8582adc6208aa6c116b1778ff531dd2b6817a10d66 9dff803240c099ba5386910b69c3b7ca8d0b26ec9a5684fe92af9c6d8068ace0954eb8582adc6208aa6c116b1778ff531dd2b6817a10d66
7e0385d18ac7ccb47eb67f1f54b5b94c84d65529c6e9fe9039218ae9cde123f38ffa52a953ed589d0d8bb48680aeea6544b7ceedbea3216 7e0385d18ac7ccb47eb67f1f54b5b94c84d65529c6e9fe9039218ae9cde123f38ffa52a953ed589d0d8bb48680aeea6544b7ceedbea3216
@ -226,7 +225,7 @@ f72aea07e183577f591a081845ea340797b79c6c8742e8826a6d7df773ec8564025fbb570bfbc001
a5d4483f1 a5d4483f1
''') ''')
db_write_enable=unhex(''' db_write_enable = unhex('''
06020000015ddf5d76a8e3bab8b10d9f82f0b31652d5b208dff1d20745b7c5442e09db880c80ca103d5b2071d8bdcaab452b900d1785be2 06020000015ddf5d76a8e3bab8b10d9f82f0b31652d5b208dff1d20745b7c5442e09db880c80ca103d5b2071d8bdcaab452b900d1785be2
de478669e72a9d082273f0f6176b700ffb65a026af052e76de5f9e247e994bed7c79e34815b465a8070a76da7d365525d7285dfb152d24c de478669e72a9d082273f0f6176b700ffb65a026af052e76de5f9e247e994bed7c79e34815b465a8070a76da7d365525d7285dfb152d24c
732a471f031d22ebb67d6e27bfff74b3ab1c4780461a460c0ea5710fbd32ab59a6ca9ca3e3e24264640b15245a6c48b6bd20c6848c2307e 732a471f031d22ebb67d6e27bfff74b3ab1c4780461a460c0ea5710fbd32ab59a6ca9ca3e3e24264640b15245a6c48b6bd20c6848c2307e
@ -262,7 +261,7 @@ a2da95eb8445a6381f3941917bed7fcf01ab199228eadd1968847e4b9a40022d2b0ec68318abf97c
''') ''')
# FIXME this must be very specific to a particular device and constructed on the fly from multiple hardcoded tables: # FIXME this must be very specific to a particular device and constructed on the fly from multiple hardcoded tables:
identify_prg=unhex(''' identify_prg = unhex('''
02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210 02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210
000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020 000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020
0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2 0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2
@ -295,7 +294,7 @@ c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1
''') ''')
# The following blob has only 1 byte difference from the above # The following blob has only 1 byte difference from the above
enroll_prg=unhex(''' enroll_prg = unhex('''
02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210 02980000002300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210
000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020 000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a020
0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2 0b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc2
@ -327,7 +326,7 @@ c1e1e1c221f201d21201e1c1f34242221201f20221f201e241e241d2020221e2420231d221e211e1
81808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e 81808180818081808180818081808180818081808080808080808080807f807f807f807f7f7e7e
''') ''')
calibrate_prg=unhex(''' calibrate_prg = unhex('''
0298006103230000002000080000200080000001003200700000000080202005002420000050207736282 0298006103230000002000080000200080000001003200700000000080202005002420000050207736282
0010030200100082170000c210000482102004c210000582000005c20000060200000682005006c200129 0010030200100082170000c210000482102004c210000582000005c20000060200000682005006c200129
70200121742001887820018084202000942001809c200902a0200b19b4200000b8203b04bc201400c0200 70200121742001887820018084202000942001809c200902a0200b19b4200000b8203b04bc201400c0200
@ -365,4 +364,3 @@ c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e20202
8080808180818081808080818081808180818081808180808081808180818081808180818081808180818 8080808180818081808080818081808180818081808180808081808180818081808180818081808180818
0818081808180818081808080808080808080807f807f807f807f7f7e7e 0818081808180818081808080808080808080807f807f807f807f7f7e7e
''') ''')

View file

@ -1,7 +1,6 @@
from .util import unhex from .util import unhex
init_hardcoded=unhex(''' init_hardcoded = unhex('''
06020000014a231406e5542fc6dc3b1aedebe68f55596ad3ca13f6e019994c6f71672fff756fbde0511d09d45978b12ba415b3694a0e763 06020000014a231406e5542fc6dc3b1aedebe68f55596ad3ca13f6e019994c6f71672fff756fbde0511d09d45978b12ba415b3694a0e763
48c8cfe9dbb9abf86813fc0c67c1005519a6f87360c2fb3e12bd0a9e012b06d9f5c9b44ccc6645b0fbd47afe45c8c874fcb88fbfd18fb7a 48c8cfe9dbb9abf86813fc0c67c1005519a6f87360c2fb3e12bd0a9e012b06d9f5c9b44ccc6645b0fbd47afe45c8c874fcb88fbfd18fb7a
9b3241351f256acce689f9586a52b01f8fdcb66cdf3b340b1f9f386d58ca24fdfcdfbcebefb5f3a3c2a08357721040235a20ce1ee2f4f78 9b3241351f256acce689f9586a52b01f8fdcb66cdf3b340b1f9f386d58ca24fdfcdfbcebefb5f3a3c2a08357721040235a20ce1ee2f4f78
@ -15,7 +14,7 @@ f3135c5e78820e36653fa3db535f57c71897242939d7da50f81070ce9ab81c61af6ac29a6c6c4a5d
2999d1c0e7acf67e598696cd58cc4bdb1b7c037ee9a085f784c4 2999d1c0e7acf67e598696cd58cc4bdb1b7c037ee9a085f784c4
''') ''')
init_hardcoded_clean_slate=unhex(''' init_hardcoded_clean_slate = unhex('''
06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea 06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea
cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691 cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691
0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875 0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875
@ -32,7 +31,7 @@ b4c74c39900830abc6906a1004bef1b5b7dbbbeb5ec1b22604ac86429b9f56511b746a7124c449b8
aa41249faeef4d838e280cb5d6fc19cfe86c75f aa41249faeef4d838e280cb5d6fc19cfe86c75f
''') ''')
reset_blob=unhex(''' reset_blob = unhex('''
06020000018772bd56dd58d64023e1745f7c253a49b32dd6a02bc32347195b6763bfcc23c9e0beb0c5809e06a5628629f28c4048530a5cd 06020000018772bd56dd58d64023e1745f7c253a49b32dd6a02bc32347195b6763bfcc23c9e0beb0c5809e06a5628629f28c4048530a5cd
df6f48391ea0c2cb5a7ffe93ef04c8b4dad584117e65aac085c25062a0f12a8ee432c7ecbb6613c28b743e4a75e382afc6b8037e342d466 df6f48391ea0c2cb5a7ffe93ef04c8b4dad584117e65aac085c25062a0f12a8ee432c7ecbb6613c28b743e4a75e382afc6b8037e342d466
7b66a73691edc6b25698c15e78d9d67f7cc56274e99e6b7bb5fba32dd42d74dfa672f414c4a29302b30a202d00a2571d2a884169e82106c 7b66a73691edc6b25698c15e78d9d67f7cc56274e99e6b7bb5fba32dd42d74dfa672f414c4a29302b30a202d00a2571d2a884169e82106c
@ -252,7 +251,7 @@ f20332657d2ca6728b9865a6bfdc9a9055a6ab86cb782307c2c0255525884bf8060f8aed7ec034f3
20f8bb8d94784228934870046f60ac937af4957c9414bcc33895fcc183062be4e4bff56f9c89838ead8e433dc604536938 20f8bb8d94784228934870046f60ac937af4957c9414bcc33895fcc183062be4e4bff56f9c89838ead8e433dc604536938
''') ''')
db_write_enable=unhex(''' db_write_enable = unhex('''
0602000001f48001074892b6c57deb7889b5ebf86bc3040f6d91ff1f68765f046591184be08cf36c154b7ec5368139d0f9532382214379a 0602000001f48001074892b6c57deb7889b5ebf86bc3040f6d91ff1f68765f046591184be08cf36c154b7ec5368139d0f9532382214379a
ff3ffbfe4659e2f274e864bd0ad660f99e21da2bab677dbfa907a66ce110c180d2ddc5dfe40b8ed975cbedffc11631f12f8bd646a0ee82d ff3ffbfe4659e2f274e864bd0ad660f99e21da2bab677dbfa907a66ce110c180d2ddc5dfe40b8ed975cbedffc11631f12f8bd646a0ee82d
44d2a6c1ec9cfbd40f485cb3d9124376b97b4a3349b0a730adda626d8ac28ec20e886aab1b8851deee3431c4d89c8bb3e787eaa9c0323df 44d2a6c1ec9cfbd40f485cb3d9124376b97b4a3349b0a730adda626d8ac28ec20e886aab1b8851deee3431c4d89c8bb3e787eaa9c0323df

View file

@ -1,7 +1,6 @@
from .util import unhex from .util import unhex
init_hardcoded=unhex(''' init_hardcoded = unhex('''
06020000014a231406e5542fc6dc3b1aedebe68f55596ad3ca13f6e019994c6f71672fff756fbde0511d09d45978b12ba415b3694a0e763 06020000014a231406e5542fc6dc3b1aedebe68f55596ad3ca13f6e019994c6f71672fff756fbde0511d09d45978b12ba415b3694a0e763
48c8cfe9dbb9abf86813fc0c67c1005519a6f87360c2fb3e12bd0a9e012b06d9f5c9b44ccc6645b0fbd47afe45c8c874fcb88fbfd18fb7a 48c8cfe9dbb9abf86813fc0c67c1005519a6f87360c2fb3e12bd0a9e012b06d9f5c9b44ccc6645b0fbd47afe45c8c874fcb88fbfd18fb7a
9b3241351f256acce689f9586a52b01f8fdcb66cdf3b340b1f9f386d58ca24fdfcdfbcebefb5f3a3c2a08357721040235a20ce1ee2f4f78 9b3241351f256acce689f9586a52b01f8fdcb66cdf3b340b1f9f386d58ca24fdfcdfbcebefb5f3a3c2a08357721040235a20ce1ee2f4f78
@ -15,7 +14,7 @@ f3135c5e78820e36653fa3db535f57c71897242939d7da50f81070ce9ab81c61af6ac29a6c6c4a5d
2999d1c0e7acf67e598696cd58cc4bdb1b7c037ee9a085f784c4 2999d1c0e7acf67e598696cd58cc4bdb1b7c037ee9a085f784c4
''') ''')
init_hardcoded_clean_slate=unhex(''' init_hardcoded_clean_slate = unhex('''
06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea 06020000012c40c9d271378bc0912ef5dced69bd81b7fc16972c7b46e621af54a00e2cc6baca6eb83ea30222dfc6c925262006ae93412ea
cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691 cf482f2034ee7b13297474b7e1e91f279cac2ccb7195443e4dd3328cfd292ade073fcc2eaa8f07b77231130ba997f921b9be7b4fb6cc691
0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875 0d2976b3e050913b27dbe73afd6e964260b9435ebab5117e71f7cb68464d4b6f8afc7e1a421f671f5854a1d0c8ab93ed3b88b2bc1a42875
@ -32,7 +31,7 @@ b4c74c39900830abc6906a1004bef1b5b7dbbbeb5ec1b22604ac86429b9f56511b746a7124c449b8
aa41249faeef4d838e280cb5d6fc19cfe86c75f aa41249faeef4d838e280cb5d6fc19cfe86c75f
''') ''')
reset_blob=unhex(''' reset_blob = unhex('''
06020000018772bd56dd58d64023e1745f7c253a49b32dd6a02bc32347195b6763bfcc23c9e0beb0c5809e06a5628629f28c4048530a5cd 06020000018772bd56dd58d64023e1745f7c253a49b32dd6a02bc32347195b6763bfcc23c9e0beb0c5809e06a5628629f28c4048530a5cd
df6f48391ea0c2cb5a7ffe93ef04c8b4dad584117e65aac085c25062a0f12a8ee432c7ecbb6613c28b743e4a75e382afc6b8037e342d466 df6f48391ea0c2cb5a7ffe93ef04c8b4dad584117e65aac085c25062a0f12a8ee432c7ecbb6613c28b743e4a75e382afc6b8037e342d466
7b66a73691edc6b25698c15e78d9d67f7cc56274e99e6b7bb5fba32dd42d74dfa672f414c4a29302b30a202d00a2571d2a884169e82106c 7b66a73691edc6b25698c15e78d9d67f7cc56274e99e6b7bb5fba32dd42d74dfa672f414c4a29302b30a202d00a2571d2a884169e82106c
@ -252,7 +251,7 @@ f20332657d2ca6728b9865a6bfdc9a9055a6ab86cb782307c2c0255525884bf8060f8aed7ec034f3
20f8bb8d94784228934870046f60ac937af4957c9414bcc33895fcc183062be4e4bff56f9c89838ead8e433dc604536938 20f8bb8d94784228934870046f60ac937af4957c9414bcc33895fcc183062be4e4bff56f9c89838ead8e433dc604536938
''') ''')
db_write_enable=unhex(''' db_write_enable = unhex('''
0602000001f48001074892b6c57deb7889b5ebf86bc3040f6d91ff1f68765f046591184be08cf36c154b7ec5368139d0f9532382214379a 0602000001f48001074892b6c57deb7889b5ebf86bc3040f6d91ff1f68765f046591184be08cf36c154b7ec5368139d0f9532382214379a
ff3ffbfe4659e2f274e864bd0ad660f99e21da2bab677dbfa907a66ce110c180d2ddc5dfe40b8ed975cbedffc11631f12f8bd646a0ee82d ff3ffbfe4659e2f274e864bd0ad660f99e21da2bab677dbfa907a66ce110c180d2ddc5dfe40b8ed975cbedffc11631f12f8bd646a0ee82d
44d2a6c1ec9cfbd40f485cb3d9124376b97b4a3349b0a730adda626d8ac28ec20e886aab1b8851deee3431c4d89c8bb3e787eaa9c0323df 44d2a6c1ec9cfbd40f485cb3d9124376b97b4a3349b0a730adda626d8ac28ec20e886aab1b8851deee3431c4d89c8bb3e787eaa9c0323df

View file

@ -1,62 +1,70 @@
import typing import typing
from .tls import tls
from .util import assert_status
from binascii import hexlify from binascii import hexlify
from struct import pack, unpack
from .blobs import db_write_enable from .blobs import db_write_enable
from .flash import call_cleanups from .flash import call_cleanups
from .sid import * from .sid import SidIdentity, sid_from_bytes
from .tls import tls
from .util import assert_status
from .winbio_constants import finger_names from .winbio_constants import finger_names
class UserStorage():
def __init__(self, dbid, name): class UserStorage:
self.dbid=dbid def __init__(self, dbid: int, name: str):
self.name=name self.dbid = dbid
self.users=[] self.name = name
self.users = []
def __repr__(self): def __repr__(self):
return '<UserStorage: dbid=%04x name=%s users=%s>' % (self.dbid, repr(self.name), repr(self.users)) return '<UserStorage: dbid=%04x name=%s users=%s>' % (self.dbid, repr(
self.name), repr(self.users))
class User():
def __init__(self, dbid, identity): class User:
self.dbid=dbid def __init__(self, dbid: int, identity: str):
self.identity=identity self.dbid = dbid
self.fingers=[] self.identity = identity
self.fingers: typing.List[typing.Mapping[str, int]] = []
def __repr__(self): def __repr__(self):
return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(self.identity), repr(self.fingers)) return '<User: dbid=%04x identity=%s fingers=%s>' % (self.dbid, repr(
self.identity), repr(self.fingers))
def subtype_to_string(s: int): def subtype_to_string(s: int):
finger_name = finger_names.get(s, None) finger_name = finger_names.get(s, None)
return finger_name or 'Unknown' return finger_name or 'Unknown'
def parse_user_storage(rsp):
def parse_user_storage(rsp: bytes):
rc, = unpack('<H', rsp[:2]) rc, = unpack('<H', rsp[:2])
if rc == 0x04b3: if rc == 0x04b3:
return None return None
assert_status(rsp[:2]) assert_status(rsp[:2])
rsp=rsp[2:] rsp = rsp[2:]
hdr, rsp = rsp[:8], rsp[8:] hdr, rsp = rsp[:8], rsp[8:]
recid, usercnt, namesz, unknwn = unpack('<HHHH', hdr) recid, usercnt, namesz, unknwn = unpack('<HHHH', hdr)
usrtab, rsp = rsp[:4*usercnt], rsp[4*usercnt:] usrtab, rsp = rsp[:4 * usercnt], rsp[4 * usercnt:]
name, rsp = rsp[:namesz], rsp[namesz:] name, rsp = rsp[:namesz], rsp[namesz:]
if len(rsp) > 0: if len(rsp) > 0:
raise Exception('Junk at the end of the storage info response: %s' % rsp.hex()) raise Exception('Junk at the end of the storage info response: %s' % rsp.hex())
storage=UserStorage(recid, name) storage = UserStorage(recid, name)
while len(usrtab) > 0: while len(usrtab) > 0:
rec, usrtab = usrtab[:4], usrtab[4:] rec, usrtab = usrtab[:4], usrtab[4:]
urid, valsz = unpack('<HH', rec) urid, valsz = unpack('<HH', rec)
storage.users += [ { 'dbid': urid, 'valueSize': valsz } ] storage.users += [{'dbid': urid, 'valueSize': valsz}]
return storage return storage
def parse_identity(b):
def parse_identity(b: bytes):
t, b = b[:4], b[4:] t, b = b[:4], b[4:]
t, = unpack('<L', t) t, = unpack('<L', t)
@ -65,45 +73,48 @@ def parse_identity(b):
l, = unpack('<L', l) l, = unpack('<L', l)
return sid_from_bytes(b[:l]) return sid_from_bytes(b[:l])
raise Exception('Don''t know how to handle identity type %d' % t) raise Exception('Don' 't know how to handle identity type %d' % t)
def parse_user(rsp: bytes): def parse_user(rsp: bytes):
assert_status(rsp[:2]) assert_status(rsp[:2])
rsp=rsp[2:] rsp = rsp[2:]
hdr, rsp = rsp[:8], rsp[8:] hdr, rsp = rsp[:8], rsp[8:]
recid, fingercnt, unknwn, identitysz = unpack('<HHHH', hdr) recid, fingercnt, unknwn, identitysz = unpack('<HHHH', hdr)
fingertab, rsp = rsp[:8*fingercnt], rsp[8*fingercnt:] fingertab, rsp = rsp[:8 * fingercnt], rsp[8 * fingercnt:]
identity, rsp = rsp[:identitysz], rsp[identitysz:] identity, rsp = rsp[:identitysz], rsp[identitysz:]
if len(rsp) > 0: if len(rsp) > 0:
raise Exception('Junk at the end of the user info response: %s' % rsp.hex()) raise Exception('Junk at the end of the user info response: %s' % rsp.hex())
identity = parse_identity(identity) identity = parse_identity(identity)
user=User(recid, identity) user = User(recid, identity)
while len(fingertab) > 0: while len(fingertab) > 0:
rec, fingertab = fingertab[:8], fingertab[8:] rec, fingertab = fingertab[:8], fingertab[8:]
frid, subtype, stgid, valsz = unpack('<HHHH', rec) frid, subtype, stgid, valsz = unpack('<HHHH', rec)
user.fingers += [ { 'dbid': frid, 'subtype': subtype, 'storage': stgid, 'valueSize': valsz } ] user.fingers += [{'dbid': frid, 'subtype': subtype, 'storage': stgid, 'valueSize': valsz}]
return user return user
def identity_to_bytes(identity: str): def identity_to_bytes(identity: str):
if isinstance(identity, SidIdentity): if isinstance(identity, SidIdentity):
b=identity.to_bytes() b = identity.to_bytes()
b = pack('<LL', 3, len(b)) + b b = pack('<LL', 3, len(b)) + b
# May not be neccessary, but windows union has a minimum size of 0x4c bytes # May not be neccessary, but windows union has a minimum size of 0x4c bytes
# and search by identity treats same SIDs with different sizes as different keys # and search by identity treats same SIDs with different sizes as different keys
while len(b) < 0x4c: while len(b) < 0x4c:
b += b'\0' b += b'\0'
return b return b
else: else:
raise Exception('Don''t know how to handle identity %s' % repr(identity)) raise Exception('Don' 't know how to handle identity %s' % repr(identity))
class DbRecord():
class DbRecord:
def __init__(self): def __init__(self):
self.dbid = 0 self.dbid = 0
self.type = 0 self.type = 0
@ -113,29 +124,24 @@ class DbRecord():
def __repr__(self): def __repr__(self):
return '<DbRecord: dbid=%d type=%d storage=%d value=%s children=%s>' % ( return '<DbRecord: dbid=%d type=%d storage=%d value=%s children=%s>' % (
self.dbid, self.dbid, self.type, self.storage, repr(self.value), repr(self.children))
self.type,
self.storage,
repr(self.value),
repr(self.children)
)
class Db():
class Info(): class Db:
def __init__(self, total, used, free, records, roots): class Info:
self.total = total # partition size def __init__(self, total: int, used: int, free: int, records: int, roots):
self.used = used # used (not deleted) self.total = total # partition size
self.free = free # unallocated space self.used = used # used (not deleted)
self.records = records # total number, including deleted self.free = free # unallocated space
self.records = records # total number, including deleted
self.roots = roots self.roots = roots
def __repr__(self): def __repr__(self):
return 'Db.Info(total=%d, used=%d, free=%d, records=%d, roots=%s)' % ( return 'Db.Info(total=%d, used=%d, free=%d, records=%d, roots=%s)' % (
self.total, self.used, self.free, self.records, repr(self.roots)) self.total, self.used, self.free, self.records, repr(self.roots))
def get_user_storage(self, dbid=0, name=''): def get_user_storage(self, dbid=0, name=''):
name=name.encode() name = name.encode()
if len(name) > 0: if len(name) > 0:
name += b'\0' name += b'\0'
@ -148,15 +154,15 @@ class Db():
def get_storage_data(self): def get_storage_data(self):
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
rc = self.get_record_children(stg.dbid).children rc = self.get_record_children(stg.dbid).children
return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type return [i['dbid'] for i in rc if i['type'] == 8] # 8 == "data" type
def get_user(self, dbid): def get_user(self, dbid: int):
return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0))) return parse_user(tls.cmd(pack('<BHHH', 0x4a, dbid, 0, 0)))
def lookup_user(self, identity: str) -> typing.Optional[User]: def lookup_user(self, identity: str) -> typing.Optional[User]:
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
data = identity_to_bytes(identity) data = identity_to_bytes(identity)
rsp = tls.cmd(pack('<BHHH', 0x4a, 0, stg.dbid, len(data)) + data) rsp = tls.cmd(pack('<BHHH', 0x4a, 0, stg.dbid, len(data)) + data)
rc, = unpack('<H', rsp[:2]) rc, = unpack('<H', rsp[:2])
@ -165,31 +171,31 @@ class Db():
else: else:
return parse_user(rsp) return parse_user(rsp)
def get_record_value(self, dbid): def get_record_value(self, dbid: int):
rsp = tls.cmd(pack('<BH', 0x49, dbid)) rsp = tls.cmd(pack('<BH', 0x49, dbid))
assert_status(rsp) assert_status(rsp)
rec = DbRecord() rec = DbRecord()
rec.dbid, rec.type, rec.storage, sz = unpack('<xxHHHHxx', rsp[:12]) rec.dbid, rec.type, rec.storage, sz = unpack('<xxHHHHxx', rsp[:12])
rec.value = rsp[12:12+sz] rec.value = rsp[12:12 + sz]
return rec return rec
def get_record_children(self, dbid): def get_record_children(self, dbid: int):
rsp = tls.cmd(pack('<BH', 0x46, dbid)) rsp = tls.cmd(pack('<BH', 0x46, dbid))
assert_status(rsp) assert_status(rsp)
rec = DbRecord() rec = DbRecord()
rec.dbid, rec.type, rec.storage, sz, cnt = unpack('<xxHHHHHxx', rsp[:14]) rec.dbid, rec.type, rec.storage, sz, cnt = unpack('<xxHHHHHxx', rsp[:14])
rsp = rsp[14:] rsp = rsp[14:]
rec.children=[] rec.children = []
for i in range(0, cnt): for i in range(0, cnt):
dbid, typ = unpack('<HH', rsp[i*4:i*4+4]) dbid, typ = unpack('<HH', rsp[i * 4:i * 4 + 4])
rec.children += [{ 'dbid': dbid, 'type': typ }] rec.children += [{'dbid': dbid, 'type': typ}]
return rec return rec
def del_record(self, dbid): def del_record(self, dbid: int):
assert_status(tls.cmd(pack('<BH', 0x48, dbid))) assert_status(tls.cmd(pack('<BH', 0x48, dbid)))
def db_info(self): def db_info(self):
@ -200,12 +206,12 @@ class Db():
unknown1, unknown0, total, used, free, records, nroots = unpack('<LLLLLHH', rsp[:0x18]) unknown1, unknown0, total, used, free, records, nroots = unpack('<LLLLLHH', rsp[:0x18])
# Seems to always be unknown1 == 1, unknown0 == 0 # Seems to always be unknown1 == 1, unknown0 == 0
rsp = rsp[0x18:] rsp = rsp[0x18:]
roots = [unpack('<H', rsp[i*2:i*2+2])[0] for i in range(0, nroots)] roots = [unpack('<H', rsp[i * 2:i * 2 + 2])[0] for i in range(0, nroots)]
return Db.Info(total, used, free, records, roots) return Db.Info(total, used, free, records, roots)
def new_record(self, parent, typ, storage, data): def new_record(self, parent: int, typ: int, storage: int, data: bytes):
self.db_info() # TODO check free space, compact the partition when out of storage self.db_info() # TODO check free space, compact the partition when out of storage
assert_status(tls.cmd(db_write_enable)) assert_status(tls.cmd(db_write_enable))
try: try:
rsp = tls.cmd(pack('<BHHHH', 0x47, parent, typ, storage, len(data)) + data) rsp = tls.cmd(pack('<BHHHH', 0x47, parent, typ, storage, len(data)) + data)
@ -215,21 +221,21 @@ class Db():
call_cleanups() call_cleanups()
return recid return recid
def new_user(self, identity): def new_user(self, identity: str):
data = identity_to_bytes(identity) data = identity_to_bytes(identity)
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
rec = self.new_record(stg.dbid, 5, stg.dbid, data) rec = self.new_record(stg.dbid, 5, stg.dbid, data)
return rec return rec
def new_finger(self, userid, template): def new_finger(self, userid: int, template: bytes):
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
# We ask to create an object of type 0xb, # We ask to create an object of type 0xb,
# but because of the magical `db_write_enable` in the new_record() it ends up being 0x6 # but because of the magical `db_write_enable` in the new_record() it ends up being 0x6
rec = self.new_record(userid, 0xb, stg.dbid, template) rec = self.new_record(userid, 0xb, stg.dbid, template)
return rec return rec
def new_data(self, parent, data): def new_data(self, parent: int, data: bytes):
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
data = pack('<HH', 1, len(data)) + data data = pack('<HH', 1, len(data)) + data
rec = self.new_record(parent, 0x8, stg.dbid, data) rec = self.new_record(parent, 0x8, stg.dbid, data)
@ -240,11 +246,11 @@ class Db():
val = hexlify(rec.value).decode() val = hexlify(rec.value).decode()
if len(val) > 80: if len(val) > 80:
val = val[:80] + '...' val = val[:80] + '...'
print('%s%d (type %d) %s' % (' '*depth, rec.dbid, rec.type, val)) print('%s%d (type %d) %s' % (' ' * depth, rec.dbid, rec.type, val))
rec = self.get_record_children(root) rec = self.get_record_children(root)
for c in rec.children: for c in rec.children:
self.dump_raw(c['dbid'], depth+1) self.dump_raw(c['dbid'], depth + 1)
def dump_all(self): def dump_all(self):
stg = self.get_user_storage(name='StgWindsor') stg = self.get_user_storage(name='StgWindsor')
@ -252,7 +258,8 @@ class Db():
for u in usrs: for u in usrs:
print('%2d: User %s with %d fingers:' % (u.dbid, repr(u.identity), len(u.fingers))) print('%2d: User %s with %d fingers:' % (u.dbid, repr(u.identity), len(u.fingers)))
for f in u.fingers: for f in u.fingers:
print(' %2d: %02x (%s)' % (f['dbid'], f['subtype'], subtype_to_string(f['subtype']))) print(' %2d: %02x (%s)' %
(f['dbid'], f['subtype'], subtype_to_string(f['subtype'])))
db = Db() db = Db()

View file

@ -1,50 +1,66 @@
from .tls import tls import typing
from struct import pack, unpack from struct import pack, unpack
from .util import assert_status, unhex
from .blobs import db_write_enable from .blobs import db_write_enable
from .hw_tables import flash_ic_table_lookup from .hw_tables import flash_ic_table_lookup, FlashIcInfo
from .tls import tls
from .util import assert_status, unhex
class FlashInfo():
def __init__(self, ic, blocks, unknown0, blocksize, unknown1, partitions):
self.ic, self.blocks, self.unknown0, self.blocksize, self.unknown1, self.partitions = ic, blocks, unknown0, blocksize, unknown1, partitions
def __repr__(self):
return 'FlashInfo(%s, 0x%x, 0x%x, 0x%x, 0x%x, %s)' % (repr(self.ic), self.blocks, self.unknown0, self.blocksize, self.unknown1, repr(self.partitions))
# type 01 is for the firmware - written blocks are decrypted on the fly using fw key # type 01 is for the firmware - written blocks are decrypted on the fly using fw key
# access lvl: # access lvl:
# 2 -- write only # 2 -- write only
# 7 -- can read/write even without TLS # 7 -- can read/write even without TLS
class PartitionInfo(): class PartitionInfo:
def __init__(self, id, type, access_lvl, offset, size): def __init__(self, id: int, type: int, access_lvl: int, offset: int, size: int):
self.id, self.type, self.access_lvl, self.offset, self.size = id, type, access_lvl, offset, size self.id, self.type, self.access_lvl, self.offset, self.size = id, type, access_lvl, offset, size
def __repr__(self): def __repr__(self):
return 'PartitionInfo(0x%02x, 0x%02x, 0x%04x, 0x%08x, 0x%08x)' % (self.id, self.type, self.access_lvl, self.offset, self.size) return 'PartitionInfo(0x%02x, 0x%02x, 0x%04x, 0x%08x, 0x%08x)' % (
self.id, self.type, self.access_lvl, self.offset, self.size)
class FlashInfo:
def __init__(self, ic: FlashIcInfo, blocks: int, unknown0: int, blocksize: int, unknown1: int,
partitions: typing.Sequence[PartitionInfo]):
self.ic = ic
self.blocks = blocks
self.unknown0 = unknown0
self.blocksize = blocksize
self.unknown1 = unknown1
self.partitions = partitions
def __repr__(self):
return 'FlashInfo(%s, 0x%x, 0x%x, 0x%x, 0x%x, %s)' % (repr(
self.ic), self.blocks, self.unknown0, self.blocksize, self.unknown1,
repr(self.partitions))
def get_flash_info(): def get_flash_info():
rsp=tls.cmd(unhex('3e')) rsp = tls.cmd(unhex('3e'))
assert_status(rsp) assert_status(rsp)
rsp=rsp[2:] rsp = rsp[2:]
hdr=rsp[:0xe] hdr = rsp[:0xe]
rsp=rsp[0xe:] rsp = rsp[0xe:]
jid0, jid1, blocks, unknown0, blocksize, unknown1, pcnt = unpack('<HHHHHHH', hdr) jid0, jid1, blocks, unknown0, blocksize, unknown1, pcnt = unpack('<HHHHHHH', hdr)
ic=flash_ic_table_lookup(jid0, jid1, blocks*blocksize)
if ic == None: ic = flash_ic_table_lookup(jid0, jid1, blocks * blocksize)
raise Exception('Unknown flash IC. JEDEC id=%x:%x, size=%dx%d' % (jid0, jid1, blocks, blocksize))
partitions=[rsp[i*0xc:(i+1)*0xc] for i in range(0, pcnt)] if ic is None:
partitions=[unpack('<BBHLL', i) for i in partitions] raise Exception('Unknown flash IC. JEDEC id=%x:%x, size=%dx%d' %
partitions=[PartitionInfo(*i) for i in partitions] (jid0, jid1, blocks, blocksize))
partitions = [rsp[i * 0xc:(i + 1) * 0xc] for i in range(0, pcnt)]
partitions = [unpack('<BBHLL', i) for i in partitions]
partitions = [PartitionInfo(*i) for i in partitions]
return FlashInfo(ic, blocks, unknown0, blocksize, unknown1, partitions) return FlashInfo(ic, blocks, unknown0, blocksize, unknown1, partitions)
# >>> 4302 -- get partition header (get fwext info) # >>> 4302 -- get partition header (get fwext info)
# b004 -- no fw detected # b004 -- no fw detected
# 0000 # 0000
# 0100 (major) 0100 (minor) 0800 (modules) c28c745a (buildtime) # 0100 (major) 0100 (minor) 0800 (modules) c28c745a (buildtime)
# type subtype major minor size # type subtype major minor size
# 0100 3446 0200 0700 d03e0000 # 0100 3446 0200 0700 d03e0000
# 0100 8408 0100 0700 00040000 # 0100 8408 0100 0700 00040000
@ -54,64 +70,74 @@ def get_flash_info():
# 0200 2377 0000 0100 802f0000 # 0200 2377 0000 0100 802f0000
# 0200 6637 0100 0c00 f0220200 # 0200 6637 0100 0c00 f0220200
# 0100 2556 0000 0100 60040000 # 0100 2556 0000 0100 60040000
class ModuleInfo(): class ModuleInfo:
def __init__(self, type, subtype, major, minor, size): def __init__(self, type: int, subtype: int, major: int, minor: int, size: int):
self.type, self.subtype, self.major, self.minor, self.size = type, subtype, major, minor, size self.type, self.subtype, self.major, self.minor, self.size = type, subtype, major, minor, size
def __repr__(self): def __repr__(self):
return 'ModuleInfo(0x%04x, 0x%04x, %d, %d, %d)' % (self.type, self.subtype, self.major, self.minor, self.size) return 'ModuleInfo(0x%04x, 0x%04x, %d, %d, %d)' % (self.type, self.subtype, self.major,
self.minor, self.size)
class FirmwareInfo():
def __init__(self, major, minor, buildtime, modules): class FirmwareInfo:
def __init__(self, major: int, minor: int, buildtime: int,
modules: typing.Sequence[ModuleInfo]):
self.major, self.minor, self.buildtime, self.modules = major, minor, buildtime, modules self.major, self.minor, self.buildtime, self.modules = major, minor, buildtime, modules
def __repr__(self): def __repr__(self):
return 'FirmwareInfo(%d, %d, %d, %s)' % (self.major, self.minor, self.buildtime, repr(self.modules)) return 'FirmwareInfo(%d, %d, %d, %s)' % (self.major, self.minor, self.buildtime,
repr(self.modules))
def get_fw_info(partition):
rsp=tls.cmd(pack('<BB', 0x43, partition)) def get_fw_info(partition: int):
rsp = tls.cmd(pack('<BB', 0x43, partition))
# don't want to throw exception here - it is normal not to have FW when we're about to upload it # don't want to throw exception here - it is normal not to have FW when we're about to upload it
if len(rsp) == 2 and rsp[1] == 4 and rsp[0] == 0xb0: if len(rsp) == 2 and rsp[1] == 4 and rsp[0] == 0xb0:
return None return None
assert_status(rsp) assert_status(rsp)
rsp=rsp[2:] rsp = rsp[2:]
hdr=rsp[:0xa] hdr = rsp[:0xa]
rsp=rsp[0xa:] rsp = rsp[0xa:]
major, minor, modcnt, buildtime = unpack('<HHHL', hdr) major, minor, modcnt, buildtime = unpack('<HHHL', hdr)
modules=[rsp[i*0xc:(i+1)*0xc] for i in range(0, modcnt)] modules = [rsp[i * 0xc:(i + 1) * 0xc] for i in range(0, modcnt)]
modules=[unpack('<HHHHL', i) for i in modules] modules = [unpack('<HHHHL', i) for i in modules]
modules=[ModuleInfo(*i) for i in modules] modules = [ModuleInfo(*i) for i in modules]
return FirmwareInfo(major, minor, buildtime, modules) return FirmwareInfo(major, minor, buildtime, modules)
def write_enable(): def write_enable():
assert_status(tls.cmd(db_write_enable)) assert_status(tls.cmd(db_write_enable))
def call_cleanups(): def call_cleanups():
rsp = tls.cmd(b'\x1a') rsp = tls.cmd(b'\x1a')
err = unpack('<H', rsp[:2])[0] err = unpack('<H', rsp[:2])[0]
if err == 0x0491: # Nothing to commit if err == 0x0491: # Nothing to commit
return # don't throw an exception, just ignore return # don't throw an exception, just ignore
assert_status(rsp) assert_status(rsp)
def erase_flash(partition):
def erase_flash(partition: int):
assert_status(tls.cmd(db_write_enable)) assert_status(tls.cmd(db_write_enable))
try: try:
assert_status(tls.cmd(pack('<BB', 0x3f, partition))) assert_status(tls.cmd(pack('<BB', 0x3f, partition)))
finally: finally:
call_cleanups() call_cleanups()
def read_flash(partition, addr, size):
def read_flash(partition: int, addr: int, size: int) -> bytes:
cmd = pack('<BBBHLL', 0x40, partition, 1, 0, addr, size) cmd = pack('<BBBHLL', 0x40, partition, 1, 0, addr, size)
rsp = tls.cmd(cmd) rsp = tls.cmd(cmd)
assert_status(rsp) assert_status(rsp)
sz, = unpack('<xxLxx', rsp[:8]) sz, = unpack('<xxLxx', rsp[:8])
return rsp[8:8+sz] return rsp[8:8 + sz]
def write_flash(partition, addr, buf):
def write_flash(partition: int, addr: int, buf: bytes):
tls.cmd(db_write_enable) tls.cmd(db_write_enable)
cmd = pack('<BBBHLL', 0x41, partition, 1, 0, addr, len(buf)) + buf cmd = pack('<BBBHLL', 0x41, partition, 1, 0, addr, len(buf)) + buf
try: try:
@ -120,21 +146,25 @@ def write_flash(partition, addr, buf):
finally: finally:
call_cleanups() call_cleanups()
def write_flash_all(partition, ptr, buf):
def write_flash_all(partition: int, ptr: int, buf: bytes):
bs = 0x1000 bs = 0x1000
while len(buf) > 0: while len(buf) > 0:
chunk, buf = buf[:bs], buf[bs:] chunk, buf = buf[:bs], buf[bs:]
write_flash(partition, ptr, chunk) write_flash(partition, ptr, chunk)
ptr += len(chunk) ptr += len(chunk)
def read_flash_all(partition, start, size):
def read_flash_all(partition: int, start: int, size: int):
bs = 0x1000 bs = 0x1000
blocks = [read_flash(partition, addr, bs) for addr in range(start, start+size, bs)] blocks = [read_flash(partition, addr, bs) for addr in range(start, start + size, bs)]
return b''.join(blocks)[:size] return b''.join(blocks)[:size]
def write_fw_signature(partition, signature):
rsp=tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature) def write_fw_signature(partition: int, signature: bytes):
rsp = tls.cmd(pack('<BBxH', 0x42, partition, len(signature)) + signature)
assert_status(rsp) assert_status(rsp)
def read_tls_flash(): def read_tls_flash():
return read_flash(1, 0, 0x1000) return read_flash(1, 0, 0x1000)

View file

@ -1,81 +1,775 @@
from .table_types import SensorTypeInfo, SensorCaptureProg from .table_types import SensorTypeInfo, SensorCaptureProg
SensorTypeInfo.table=[ SensorTypeInfo.table = [
SensorTypeInfo(sensor_type=0x00db, bytes_per_line=0x98, repeat_multiplier=1, lines_per_calibration_data=144, line_width=144, calibration_blob='101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'), SensorTypeInfo(
SensorTypeInfo(sensor_type=0x00e4, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=100, line_width=112, calibration_blob='9392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e'), sensor_type=0x00db,
SensorTypeInfo(sensor_type=0x00ed, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), bytes_per_line=0x98,
SensorTypeInfo(sensor_type=0x0199, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), repeat_multiplier=1,
SensorTypeInfo(sensor_type=0x00b5, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), lines_per_calibration_data=144,
SensorTypeInfo(sensor_type=0x0885, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), line_width=144,
SensorTypeInfo(sensor_type=0x1055, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), calibration_blob=
SensorTypeInfo(sensor_type=0x1825, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), '101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
SensorTypeInfo(sensor_type=0x1ff5, bytes_per_line=0x78, repeat_multiplier=2, lines_per_calibration_data=112, line_width=112, calibration_blob='9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'), ),
SensorTypeInfo(sensor_type=0x00b3, bytes_per_line=0x60, repeat_multiplier=2, lines_per_calibration_data=84, line_width=85, calibration_blob='898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'), SensorTypeInfo(
SensorTypeInfo(sensor_type=0x143b, bytes_per_line=0x5c, repeat_multiplier=2, lines_per_calibration_data=84, line_width=84, calibration_blob='898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'), sensor_type=0x00e4,
SensorTypeInfo(sensor_type=0x08b1, bytes_per_line=0x58, repeat_multiplier=2, lines_per_calibration_data=78, line_width=78, calibration_blob='9b9a9996959392918f8e8d8b8a89878685837d7b7a7977767573716f6d6b6a695d5b5a595756555251504e4d4c4a41403e3d3c3a393432312c2a28261e1d1c1a19181615141211100d0c0a090806'), bytes_per_line=0x78,
SensorTypeInfo(sensor_type=0x00e1, bytes_per_line=0x58, repeat_multiplier=2, lines_per_calibration_data=78, line_width=78, calibration_blob='9b9a9996959392918f8e8d8b8a89878685837d7b7a7977767573716f6d6b6a695d5b5a595756555251504e4d4c4a41403e3d3c3a393432312c2a28261e1d1c1a19181615141211100d0c0a090806'), repeat_multiplier=2,
SensorTypeInfo(sensor_type=0x00ea, bytes_per_line=0x5c, repeat_multiplier=1, lines_per_calibration_data=84, line_width=84, calibration_blob='898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'), lines_per_calibration_data=100,
SensorTypeInfo(sensor_type=0x0194, bytes_per_line=0x7c, repeat_multiplier=3, lines_per_calibration_data=84, line_width=114, calibration_blob='000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253'), line_width=112,
SensorTypeInfo(sensor_type=0x0126, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), calibration_blob=
SensorTypeInfo(sensor_type=0x0117, bytes_per_line=0xa0, repeat_multiplier=4, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), '9392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e'
SensorTypeInfo(sensor_type=0x08f3, bytes_per_line=0xa0, repeat_multiplier=1, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), ),
SensorTypeInfo(sensor_type=0x08f6, bytes_per_line=0xa0, repeat_multiplier=1, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), SensorTypeInfo(
SensorTypeInfo(sensor_type=0x0121, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), sensor_type=0x00ed,
SensorTypeInfo(sensor_type=0x0b4b, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), bytes_per_line=0x78,
SensorTypeInfo(sensor_type=0x0b4d, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=56, line_width=144, calibration_blob='0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'), repeat_multiplier=2,
SensorTypeInfo(sensor_type=0x0130, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=40, line_width=144, calibration_blob='15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'), lines_per_calibration_data=112,
SensorTypeInfo(sensor_type=0x0be2, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=40, line_width=144, calibration_blob='15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'), line_width=112,
SensorTypeInfo(sensor_type=0x0be1, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=40, line_width=144, calibration_blob='15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'), calibration_blob=
SensorTypeInfo(sensor_type=0x0518, bytes_per_line=0xa0, repeat_multiplier=2, lines_per_calibration_data=40, line_width=144, calibration_blob='15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'), '9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
SensorTypeInfo(sensor_type=0x0179, bytes_per_line=0x98, repeat_multiplier=3, lines_per_calibration_data=56, line_width=144, calibration_blob='3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172'), ),
SensorTypeInfo(
sensor_type=0x0199,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x00b5,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x0885,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x1055,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x1825,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x1ff5,
bytes_per_line=0x78,
repeat_multiplier=2,
lines_per_calibration_data=112,
line_width=112,
calibration_blob=
'9b9a999796959392918f8e8d8b8a898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19181615141211100e0d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x00b3,
bytes_per_line=0x60,
repeat_multiplier=2,
lines_per_calibration_data=84,
line_width=85,
calibration_blob=
'898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'
),
SensorTypeInfo(
sensor_type=0x143b,
bytes_per_line=0x5c,
repeat_multiplier=2,
lines_per_calibration_data=84,
line_width=84,
calibration_blob=
'898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'
),
SensorTypeInfo(
sensor_type=0x08b1,
bytes_per_line=0x58,
repeat_multiplier=2,
lines_per_calibration_data=78,
line_width=78,
calibration_blob=
'9b9a9996959392918f8e8d8b8a89878685837d7b7a7977767573716f6d6b6a695d5b5a595756555251504e4d4c4a41403e3d3c3a393432312c2a28261e1d1c1a19181615141211100d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x00e1,
bytes_per_line=0x58,
repeat_multiplier=2,
lines_per_calibration_data=78,
line_width=78,
calibration_blob=
'9b9a9996959392918f8e8d8b8a89878685837d7b7a7977767573716f6d6b6a695d5b5a595756555251504e4d4c4a41403e3d3c3a393432312c2a28261e1d1c1a19181615141211100d0c0a090806'
),
SensorTypeInfo(
sensor_type=0x00ea,
bytes_per_line=0x5c,
repeat_multiplier=1,
lines_per_calibration_data=84,
line_width=84,
calibration_blob=
'898786858382817f7e7d7b7a797776757372716f6e6d6b6a696766656362615f5e5d5b5a595756555251504e4d4c4a49484645444241403e3d3c3a39383635343231302e2d2c2a29282625242221201e1d1c1a19'
),
SensorTypeInfo(
sensor_type=0x0194,
bytes_per_line=0x7c,
repeat_multiplier=3,
lines_per_calibration_data=84,
line_width=114,
calibration_blob=
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253'
),
SensorTypeInfo(
sensor_type=0x0126,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x0117,
bytes_per_line=0xa0,
repeat_multiplier=4,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x08f3,
bytes_per_line=0xa0,
repeat_multiplier=1,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x08f6,
bytes_per_line=0xa0,
repeat_multiplier=1,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x0121,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x0b4b,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x0b4d,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243'
),
SensorTypeInfo(
sensor_type=0x0130,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=40,
line_width=144,
calibration_blob=
'15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'),
SensorTypeInfo(
sensor_type=0x0be2,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=40,
line_width=144,
calibration_blob=
'15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'),
SensorTypeInfo(
sensor_type=0x0be1,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=40,
line_width=144,
calibration_blob=
'15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'),
SensorTypeInfo(
sensor_type=0x0518,
bytes_per_line=0xa0,
repeat_multiplier=2,
lines_per_calibration_data=40,
line_width=144,
calibration_blob=
'15161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3b'),
SensorTypeInfo(
sensor_type=0x0179,
bytes_per_line=0x98,
repeat_multiplier=3,
lines_per_calibration_data=56,
line_width=144,
calibration_blob=
'3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172'
),
] ]
SensorCaptureProg.table=[ SensorCaptureProg.table = [
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), SensorCaptureProg(
SensorCaptureProg(major=0x6, minor=0x6, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), major=0x6,
SensorCaptureProg(major=0x6, minor=0x7, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), minor=0x0,
SensorCaptureProg(major=0x6, minor=0x8, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), build=0x0,
SensorCaptureProg(major=0x6, minor=0xa, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), u1=0x0,
SensorCaptureProg(major=0x6, minor=0xb, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), dev_type=0x0,
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0x0, a0=0x0, a1=0x0, blobs=['230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000', '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000', '29000400000000003500040000000000']), a0=0x0,
SensorCaptureProg(major=0x6, minor=0x6, build=0x0, u1=0x0, dev_type=0x885, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082150000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000']), a1=0x0,
SensorCaptureProg(major=0x6, minor=0x7, build=0x0, u1=0x0, dev_type=0x885, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082150000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000']), blobs=[
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x1055, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482103004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000']), '23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000',
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x1825, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601062810250101000007c8078c06ff4f80006d0300200307030990098db00b90880991858e08c1810b9190910ac1b828928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f812089920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xb5, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e0034007801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001023171023171023161023161023160106635205001025010100000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), '29000400000000003500040000000000'
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x1ff5, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601062810250101000007c8078c06ff4f80006d0300200307030990098db00b90880991858e08c1810b9190910ac1b828928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f812089920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), ]),
SensorCaptureProg(major=0x6, minor=0x7, build=0x0, u1=0x0, dev_type=0x1825, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010240101000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), SensorCaptureProg(
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xe4, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221410221410221410221410221401066510250101000007c8078c06ff4f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c0703030720040200002f0004006400000029000400700000003500040080000000']), major=0x6,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xed, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482104004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010240101000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), minor=0x6,
SensorCaptureProg(major=0x6, minor=0x7, build=0x0, u1=0x0, dev_type=0x143b, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000d600d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c1117003400340200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101c11101c11101c11101c11101c10010657101e0101000007c8078c06ff00004f80006d03002c03070308c608c709930703800ac60994800ac70995800b930996800b940997800b950998800b9608c8800b9708ca800b9808cb800ac808cc800aca08ce800acb08cf800acc099a800ace099b800acf099c800b9a099d800b9b099e800b9c099f800b9d08d0800b9e08d2800b9f08d3800ad008d4800ad208d6800ad308d7800ad40802800ad60803800ad7095a800a02095b800a03095c800b5a095d800b5b08d9800b5c08da800b5d08db800ad908dd800ada08de800adb08df800add095e800ade095f800adf0960800b5e0961800b5f0962800b600963800b6108e1800b6208e2800b6308e3800ae108e5800ae208e6800ae308e7800ae50964800ae60965800ae70966800b640967800b650968800b66096e800b6708e9800b6808ea800b6e08eb800ae908ed800aea08ef800aeb08ee800aed096f800aef0970800aee0971800b6f0972800b700974800b710975800b7208f0800b7408f1800b7508f2800af008f4800af108f5800af208f6800af40976800af50977800af60979800b760978800b77097a800b79097b800b7808f8800b7a08fa800b7b08fc800af808fd800afa08fe800afc0900800c2007030307200402002f0004005400000029000400580000003500040068000000']), build=0x0,
SensorCaptureProg(major=0x6, minor=0x7, build=0x0, u1=0x0, dev_type=0xb3, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082140000c210000482106004c210000582000005c2000006020000068200a006c20012970200121742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200100c4200100c82002003300100000000080cc2000002c01d0200000a1013200440000000080dc205302e0206401e420a901e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c11180034003c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101c11101c11101c11101c11101c10010657101e0101000007c8078c06ff00004f80006d03002c0308c608c70703800ac60993800ac70994800b930995800b940996800b950997800b960998800b9708c8800b9808ca800ac808cb800aca08cc800acb08ce800acc08cf800ace099a800acf099b800b9a099c800b9b099d800b9c099e800b9d099f800b9e08d0800b9f08d2800ad008d3800ad208d4800ad308d6800ad408d7800ad60802800ad70803800a02095a800a03095b800b5a095c800b5b095d800b5c08d9800b5d08da800ad908db800ada08dd800adb08de800add08df800ade095e800adf095f800b5e0960800b5f0961800b600962800b610963800b6208e1800b6308e2800ae108e3800ae208e5800ae308e6800ae508e7800ae60964800ae70965800b640966800b650967800b660968800b67096e800b6808e9800b6e08ea800ae908eb800aea08ed800aeb08ef800aed08ee800aef096f800aee0970800b6f0971800b700972800b710974800b720975800b7408f0800b7508f1800af008f2800af108f4800af208f5800af408f6800af50976800af60977800b760979800b770978800b79097a800b78097b800b7a08f8800b7b08fa800af808fc800afa08fd800afc08fe800afd090080808080240c0703030720040200000000002f0004005400000029000400580000003500040068000000']), u1=0x0,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xe1, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032006c000000008020200400242000005020773628200100302001003c208000082138000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084203000942001809c200902a0200b19bc201400c0200200c4200100c82002003300100000000080cc2000006401d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c11160034001402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b10101b10101b10101b0f101b0f010656101e0101000007c8078c06ff4f80006d03002c03070308c108bf098f0703800ac10990800abf098d800b8f0991800b900992800b8d08c5800b9108c3800b9208c6800ac508c4800ac308cb800ac608c7800ac40993800acb0994800ac70995800b930997800b940996800b950998800b9708ce800b9608cc800b9808cf800ace08d0800acc08d2800acf08dd800ad0099a800ad2099b800add099c800b9a0803800b9b095a800b9c095b800a0308de800b5a08df800b5b08e1800ade08eb800adf08ed800ae108f1800aeb095c800aed0962800af10963800b5c096f800b620970800b630971800b6f08f2800b7008f5800b7108f6800af208fa800af508f8800af608fc800afa0974800af80976800afc097b800b74097a800b76097f800b7b097e800b7a08fd800b7f08fe800b7e090b800afd090c800afe090d800b0b090f800b0c0980800b0d0982800b0f0983800b80097d800b820978800b83097c800b7d0914800b780908800b7c0909800b140907800b080905800b090904800b070979800b050977800b040975800b790972800b77096c800b750936800b7209348080800c20070303072004020000002f0004004e00000029000400540000003500040064000000']), dev_type=0x0,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x8b1, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032006c000000008020200400242000005020773628200100302001003c208000082138000c210000482107004c210000582000005c2000006020000068200a006c20014970200141742001887820018084203000942001809c200902a0200b19bc201400c0200200c4200100c82002003300100000000080cc2000002c00d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c11160034001402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b10101b10101b10101b0f101b0f010656101e0101000007c8078c06ff4f80006d03002c03070308c108bf098f0703800ac10990800abf098d800b8f0991800b900992800b8d08c5800b9108c3800b9208c6800ac508c4800ac308cb800ac608c7800ac40993800acb0994800ac70995800b930997800b940996800b950998800b9708ce800b9608cc800b9808cf800ace08d0800acc08d2800acf08dd800ad0099a800ad2099b800add099c800b9a0803800b9b095a800b9c095b800a0308de800b5a08df800b5b08e1800ade08eb800adf08ed800ae108f1800aeb095c800aed0962800af10963800b5c096f800b620970800b630971800b6f08f2800b7008f5800b7108f6800af208fa800af508f8800af608fc800afa0974800af80976800afc097b800b74097a800b76097f800b7b097e800b7a08fd800b7f08fe800b7e090b800afd090c800afe090d800b0b090f800b0c0980800b0d0982800b0f0983800b80097d800b820978800b83097c800b7d0914800b780908800b7c0909800b140907800b080905800b090904800b070979800b050977800b040975800b790972800b77096c800b750936800b7209348080800c20070303072004020000002f0004004e00000029000400540000003500040064000000']), a0=0x0,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xea, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000d600d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c11170034003002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b11101b11101b11101b11101b1001064d101d0101000007c8078c06ff00004f80006d03002c03070308c608c709930703800ac60994800ac70995800b930996800b940997800b950998800b9608c8800b9708ca800b9808cb800ac808cc800aca08ce800acb08cf800acc099a800ace099b800acf099c800b9a099d800b9b099e800b9c099f800b9d08d0800b9e08d2800b9f08d3800ad008d4800ad208d6800ad308d7800ad40802800ad60803800ad7095a800a02095b800a03095c800b5a095d800b5b08d9800b5c08da800b5d08db800ad908dd800ada08de800adb08df800add095e800ade095f800adf0960800b5e0961800b5f0962800b600963800b6108e1800b6208e2800b6308e3800ae108e5800ae208e6800ae308e7800ae50964800ae60965800ae70966800b640967800b650968800b66096e800b6708e9800b6808ea800b6e08eb800ae908ed800aea08ef800aeb08ee800aed096f800aef0970800aee0971800b6f0972800b700974800b710975800b7208f0800b7408f1800b7508f2800af008f4800af108f5800af208f6800af40976800af50977800af60979800b760978800b77097a800b79097b800b7808f8800b7a08fa800b7b08fc800af808fd800afa08fe800afc0900800c2007030307200402002f0004005400000029000400540000003500040064000000']), a1=0x0,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x199, a0=0x18, a1=0x19, blobs=['23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482107004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c111e00340078010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010250101000007c8078c06ff0000000000004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000']), blobs=[
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0x8f3, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032006000000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20700070207000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002a0030000330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c64200050630c642000606c0c64200070620c642000806b0c64200090610c642000a06a0c642000b0600c642000c0690c642000d05f0c642000e05e0c642000f04f0c642000005d0c642000104e0c642000205c0c642000304d0c642000405b0c642000504c0c642000605a0c642000704b0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d10c642000d02b0c642000e0d20c642000f02a0c64200000d30c64200010290c64200020d60c64200030280c64200040d70c642000501d0c64200060d80c642000701c0c64200080d90c642000901b0c642000a0da0c642000b01a0c642000c0de0c642000d0190c642000e0df0c642000f0150c64200000e00c64200010140c64200020e10c64200030130c64200040e20c64200050120c64200060e40c64200070110c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b070100002403089808a9089908a8089a08a7089b08a6089c08a4089d08a3089e08a2089f08a1c0102c03088808b9088908b8088a08b7088b08b3089208b2089308b1089408b0089508afc01003087108c6087208c5087d08c2087e08c1087f08c0088008bf088108bb088708bac0100628070103084f085e085f08690860086a0861086b0862086c086308c9086f08c8087008c7c010030847082e0848082f08490830084a0831084b085a084c085b084d085c084e085dc01003082a08d2082b08d1082c08ce083808cd083908cc083a08cb083b08ca0846082dc0100632070103081508df081908de081a08da081b08d9081c08d8081d08d7082808d6082908d3c01059940b03080a08e8080b08e7080c08e6080d08e5081108e4081208e2081308e1081408e0c0102403080008f1080108f0080208ef080308ee080408ed080508ec080608eb080708eac0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), '23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000',
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0x8f6, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20500070205000742000007820000084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c64200050630c642000606c0c64200070620c642000806b0c64200090610c642000a06a0c642000b0600c642000c0690c642000d05f0c642000e05e0c642000f04f0c642000005d0c642000104e0c642000205c0c642000304d0c642000405b0c642000504c0c642000605a0c642000704b0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c0000000080681101006411260034001c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b070100000720240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c90863086c0862086b0861086a08600869085f085e084fc01003085d084e085c084d085b084c085a084b0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c0100308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c010070320030c07030703072004020000004200080002089000000000002f00040038000000', '2f00040038000000290004009000000035000400a0000000']), '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0x121, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20720070207200742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a120100000620102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063007010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), '29000400000000003500040000000000'
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0x121, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20720070207200742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a120100000620102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063007010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), ]),
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0xb4b, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20660070206600742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), SensorCaptureProg(
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0xb4b, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20660070206600742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), major=0x6,
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0xb4b, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050201f015c200000602000004c2003006c20720070207200742004007820040084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d03005e03004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), minor=0x7,
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0xb4b, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050201f015c200000602000004c2003006c20720070207200742004007820040084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d03005e03004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), build=0x0,
SensorCaptureProg(major=0x6, minor=0x14, build=0x0, u1=0x0, dev_type=0xb4d, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20600070206000742002007820020084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d01005e01004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), u1=0x0,
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0xb4d, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20600070206000742002007820020084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c', '32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d01005e01004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000']), dev_type=0x0,
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0x130, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20400070204000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003', '330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c', '32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000']), a0=0x0,
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0x518, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20640070206400742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003', '330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c', '32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000']), a1=0x0,
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0xbe1, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20550070205500742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003', '330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c', '32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000']), blobs=[
SensorCaptureProg(major=0x6, minor=0x15, build=0x0, u1=0x0, dev_type=0xbe2, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20500070205000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003', '330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c', '32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000']), '23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000',
SensorCaptureProg(major=0x6, minor=0xb, build=0x0, u1=0x0, dev_type=0x194, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032005c0000000080202004002820ff0b2c20f60b6c202f007020000074201000782000005c2000006020000064200000502000005420151d84202000942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc2000004900d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0201400f8200500fc200000b8203b00002804001428000008280000082810001428180008281100142819001c281a00', '32001400000000806811010064111f007c1700007017010033001c00000000809c1701000000ac1700900100b01754000000b417007200003400500010061110061110061110061110061001068010080101000007c80764060000002003070107010c07840a640863c91e2cc9159107010a9dc9110701030a3208330701c90f20c91e070304020000000000', '2f0004005400000029000400000000003500040010000000']), '2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0x179, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032007800000000802020040024200000382000002820df0b2c20df0b30200000342000003c20000040200000442000004820000050200a005c20000060200000642000004c200300002100006c2030007020300040210000742006007820060084202000b4200000b8203a00bc201400c0200200c4200100c820020033001c000000008054202f27030058202f270300cc200000ef03d0200000ef033200440000000080dc20e803e020e803e420d002e820d002f0200500f8200500fc200000b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '320014000000008068110100641126007c1700007017010033001c00000000809c1701000000ac1700900100b01738000000b417009000003400040310060b10060b10060b10060b10060c01064e10080101000007c80764060000002003070107010c2c078408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b800aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a720871800ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a800ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a060805800aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800a010800802007032003070307030402000000', '2f0004003800000029000400000000003500040010000000']), '29000400000000003500040000000000'
SensorCaptureProg(major=0x6, minor=0xa, build=0x0, u1=0x0, dev_type=0x117, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032006400000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c20680070206800742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a80063207010ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a06080580240aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800c0a01080080070103040200000000002f00040038000000', '290004009400000035000400a4000000']), ]),
SensorCaptureProg(major=0x6, minor=0xb, build=0x0, u1=0x0, dev_type=0x117, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032006400000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c20680070206800742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a80063207010ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a06080580240aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800c0a01080080070103040200000000002f00040038000000', '290004009400000035000400a4000000']), SensorCaptureProg(
SensorCaptureProg(major=0x6, minor=0xb, build=0x0, u1=0x0, dev_type=0x126, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032007000000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c2068007020680074200100782001006420000084202000b4200000b8203a00bc201400c0200200c4200100c820020074030000a0030f00330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00', '32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d0800a2c082b800ad008d1800a2b082a80063207010ad108d2800a2a0829800ad208d3800a290828800ad308d6800a28081c800ad608d7800a1c081b800ad708d8800a1b081a800ad808d9800a1a0819800ad908da800a190815800ada08df800a150814800adf08e0800a140813800ae008e1800a130812800ae108e2800a120811800ae208e4800a11080e800ae408e5800a0e080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708e8800a0b080a800ae808ea800a0a0807800aea08eb800a070806800aeb08ec800a06080580240aec08ed800a050804800aed08ee800a040803800aee08ef800a030802800aef08f0800a020801800af008f1800c0a01080080070c03040200000000002f00040038000000290004009400000035000400a4000000']), major=0x6,
SensorCaptureProg(major=0x6, minor=0x0, build=0x0, u1=0x0, dev_type=0xdb, a0=0x18, a1=0x19, blobs=['2300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a0200b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00', '32000c0000000080501101004c1126003400080310061d10061d10061d10061d10061c01065810080101000007c8078c06100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d800af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b610962800af408f3800b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08ee800b670968800aee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b700971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b750976800ae308e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7a097b800ade08dd800b7b097c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b860987800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace08cd800b8b098c800acd08cc800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c8800b900991800ac808c7800b910992800ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c1800b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd08bc800b9c099d800abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba00801800ab808b7800a010802800ab708b6800a020803800ab608b5800a030804802003070404020000000000002f0004009000000029000400000000003500040010000000']), minor=0x8,
build=0x0,
u1=0x0,
dev_type=0x0,
a0=0x0,
a1=0x0,
blobs=[
'23000000320074000000008000200000502077322820020030200000082110000c211000482105004c2105002020000024200000582000005c20000060204300682014006c2001247020012c842020008c20900190202c01942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc200000a101d0200000a10132004c0000000080dc20e803e0206401e420d002e8200001ec201400f0200500fc200000b8203a00140800000008040008080000080802001408300008080300140831001c081a004c11240050110000',
'2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
'29000400000000003500040000000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xa,
build=0x0,
u1=0x0,
dev_type=0x0,
a0=0x0,
a1=0x0,
blobs=[
'230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000',
'2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
'29000400000000003500040000000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xb,
build=0x0,
u1=0x0,
dev_type=0x0,
a0=0x0,
a1=0x0,
blobs=[
'230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000',
'2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
'29000400000000003500040000000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0x0,
a0=0x0,
a1=0x0,
blobs=[
'230000003200680000000080002000002020000024200000382000002820df0b2c20df0b302000003420000050200a005c20000064204300602000004c2000006c20100070201000742005007820050084202000b4200000b8203b00bc201400c0200200c4200100c82002007403000233001c000000008054202a2203005820272f0300cc200000ef03d0200000ef033200480000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203b00002804001428000008280000082800001428300008280000142831001c281a006411240068110000',
'2a00080020010100100100002c002800802080200000000000013f4000000000080f080f00000000279c1000279c10000000000000000000340040000300000007160000240a59085a0701c9500aaa07010ada08db0701c9460b2107010800800a0088c9590a5a07010aa908aa0701c91f0ac900000c040100000000',
'29000400000000003500040000000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x6,
build=0x0,
u1=0x0,
dev_type=0x885,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082150000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x7,
build=0x0,
u1=0x0,
dev_type=0x885,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082150000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x1055,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482103004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601066310250101000007c8078c06ff4f80006d0300200307030b90098db08d099186890b9108c180910ac190ba2c928f099382890b938889898908c881910ac8888a9189099a82890b9a8889898908d081910ad0888a9189080282890a02095a80890b5a888908d981890ad98891898a095e81890b5e8889898908e181890ae18891898a096481890b64888989096e8108e9810b6e880ae9889191ba096f828f0b6f8889918908f082890af08891898a097681890b7690b9928f08f882910af8908a8a91097c818a0b7c88090181890b018889892491097f818a0b7f900908818a0b0890898a910c0703030720040200002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x1825,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601062810250101000007c8078c06ff4f80006d0300200307030990098db00b90880991858e08c1810b9190910ac1b828928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f812089920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xb5,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e0034007801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001023171023171023161023161023160106635205001025010100000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x1ff5,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601062810250101000007c8078c06ff4f80006d0300200307030990098db00b90880991858e08c1810b9190910ac1b828928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f812089920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x7,
build=0x0,
u1=0x0,
dev_type=0x1825,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010240101000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xe4,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340070010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221410221410221410221410221401066510250101000007c8078c06ff4f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c0703030720040200002f0004006400000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xed,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482104004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340074010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010240101000007c8078c06ff00004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x7,
build=0x0,
u1=0x0,
dev_type=0x143b,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482108004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000d600d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c1117003400340200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101c11101c11101c11101c11101c10010657101e0101000007c8078c06ff00004f80006d03002c03070308c608c709930703800ac60994800ac70995800b930996800b940997800b950998800b9608c8800b9708ca800b9808cb800ac808cc800aca08ce800acb08cf800acc099a800ace099b800acf099c800b9a099d800b9b099e800b9c099f800b9d08d0800b9e08d2800b9f08d3800ad008d4800ad208d6800ad308d7800ad40802800ad60803800ad7095a800a02095b800a03095c800b5a095d800b5b08d9800b5c08da800b5d08db800ad908dd800ada08de800adb08df800add095e800ade095f800adf0960800b5e0961800b5f0962800b600963800b6108e1800b6208e2800b6308e3800ae108e5800ae208e6800ae308e7800ae50964800ae60965800ae70966800b640967800b650968800b66096e800b6708e9800b6808ea800b6e08eb800ae908ed800aea08ef800aeb08ee800aed096f800aef0970800aee0971800b6f0972800b700974800b710975800b7208f0800b7408f1800b7508f2800af008f4800af108f5800af208f6800af40976800af50977800af60979800b760978800b77097a800b79097b800b7808f8800b7a08fa800b7b08fc800af808fd800afa08fe800afc0900800c2007030307200402002f0004005400000029000400580000003500040068000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x7,
build=0x0,
u1=0x0,
dev_type=0xb3,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082140000c210000482106004c210000582000005c2000006020000068200a006c20012970200121742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200100c4200100c82002003300100000000080cc2000002c01d0200000a1013200440000000080dc205302e0206401e420a901e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c11180034003c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101c11101c11101c11101c11101c10010657101e0101000007c8078c06ff00004f80006d03002c0308c608c70703800ac60993800ac70994800b930995800b940996800b950997800b960998800b9708c8800b9808ca800ac808cb800aca08cc800acb08ce800acc08cf800ace099a800acf099b800b9a099c800b9b099d800b9c099e800b9d099f800b9e08d0800b9f08d2800ad008d3800ad208d4800ad308d6800ad408d7800ad60802800ad70803800a02095a800a03095b800b5a095c800b5b095d800b5c08d9800b5d08da800ad908db800ada08dd800adb08de800add08df800ade095e800adf095f800b5e0960800b5f0961800b600962800b610963800b6208e1800b6308e2800ae108e3800ae208e5800ae308e6800ae508e7800ae60964800ae70965800b640966800b650967800b660968800b67096e800b6808e9800b6e08ea800ae908eb800aea08ed800aeb08ef800aed08ee800aef096f800aee0970800b6f0971800b700972800b710974800b720975800b7408f0800b7508f1800af008f2800af108f4800af208f5800af408f6800af50976800af60977800b760979800b770978800b79097a800b78097b800b7a08f8800b7b08fa800af808fc800afa08fd800afc08fe800afd090080808080240c0703030720040200000000002f0004005400000029000400580000003500040068000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xe1,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032006c000000008020200400242000005020773628200100302001003c208000082138000c210000482105004c210000582000005c2000006020000068200a006c20014970200141742001887820018084203000942001809c200902a0200b19bc201400c0200200c4200100c82002003300100000000080cc2000006401d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c11160034001402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b10101b10101b10101b0f101b0f010656101e0101000007c8078c06ff4f80006d03002c03070308c108bf098f0703800ac10990800abf098d800b8f0991800b900992800b8d08c5800b9108c3800b9208c6800ac508c4800ac308cb800ac608c7800ac40993800acb0994800ac70995800b930997800b940996800b950998800b9708ce800b9608cc800b9808cf800ace08d0800acc08d2800acf08dd800ad0099a800ad2099b800add099c800b9a0803800b9b095a800b9c095b800a0308de800b5a08df800b5b08e1800ade08eb800adf08ed800ae108f1800aeb095c800aed0962800af10963800b5c096f800b620970800b630971800b6f08f2800b7008f5800b7108f6800af208fa800af508f8800af608fc800afa0974800af80976800afc097b800b74097a800b76097f800b7b097e800b7a08fd800b7f08fe800b7e090b800afd090c800afe090d800b0b090f800b0c0980800b0d0982800b0f0983800b80097d800b820978800b83097c800b7d0914800b780908800b7c0909800b140907800b080905800b090904800b070979800b050977800b040975800b790972800b77096c800b750936800b7209348080800c20070303072004020000002f0004004e00000029000400540000003500040064000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x8b1,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032006c000000008020200400242000005020773628200100302001003c208000082138000c210000482107004c210000582000005c2000006020000068200a006c20014970200141742001887820018084203000942001809c200902a0200b19bc201400c0200200c4200100c82002003300100000000080cc2000002c00d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c11160034001402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b10101b10101b10101b0f101b0f010656101e0101000007c8078c06ff4f80006d03002c03070308c108bf098f0703800ac10990800abf098d800b8f0991800b900992800b8d08c5800b9108c3800b9208c6800ac508c4800ac308cb800ac608c7800ac40993800acb0994800ac70995800b930997800b940996800b950998800b9708ce800b9608cc800b9808cf800ace08d0800acc08d2800acf08dd800ad0099a800ad2099b800add099c800b9a0803800b9b095a800b9c095b800a0308de800b5a08df800b5b08e1800ade08eb800adf08ed800ae108f1800aeb095c800aed0962800af10963800b5c096f800b620970800b630971800b6f08f2800b7008f5800b7108f6800af208fa800af508f8800af608fc800afa0974800af80976800afc097b800b74097a800b76097f800b7b097e800b7a08fd800b7f08fe800b7e090b800afd090c800afe090d800b0b090f800b0c0980800b0d0982800b0f0983800b80097d800b820978800b83097c800b7d0914800b780908800b7c0909800b140907800b080905800b090904800b070979800b050977800b040975800b790972800b77096c800b750936800b7209348080800c20070303072004020000002f0004004e00000029000400540000003500040064000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xea,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320070000000008020200400242000005020773628200100302001003c208000082138000c210000482106004c210000582000005c2000006020000068200a006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300bc201400c0200200c4200100c82002003300100000000080cc200000d600d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c11170034003002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101b11101b11101b11101b11101b1001064d101d0101000007c8078c06ff00004f80006d03002c03070308c608c709930703800ac60994800ac70995800b930996800b940997800b950998800b9608c8800b9708ca800b9808cb800ac808cc800aca08ce800acb08cf800acc099a800ace099b800acf099c800b9a099d800b9b099e800b9c099f800b9d08d0800b9e08d2800b9f08d3800ad008d4800ad208d6800ad308d7800ad40802800ad60803800ad7095a800a02095b800a03095c800b5a095d800b5b08d9800b5c08da800b5d08db800ad908dd800ada08de800adb08df800add095e800ade095f800adf0960800b5e0961800b5f0962800b600963800b6108e1800b6208e2800b6308e3800ae108e5800ae208e6800ae308e7800ae50964800ae60965800ae70966800b640967800b650968800b66096e800b6708e9800b6808ea800b6e08eb800ae908ed800aea08ef800aeb08ee800aed096f800aef0970800aee0971800b6f0972800b700974800b710975800b7208f0800b7408f1800b7508f2800af008f4800af108f5800af208f6800af40976800af50977800af60979800b760978800b77097a800b79097b800b7808f8800b7a08fa800b7b08fc800af808fd800afa08fe800afc0900800c2007030307200402002f0004005400000029000400540000003500040064000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x199,
a0=0x18,
a1=0x19,
blobs=[
'23000000200008000020008000000100320074000000008020200400242000005020773628200100302001003c208000082138000c210000482107004c210000582000005c20000060200000682005006c20014970200141742001887820018084202000942001809c200902a0200b19b4200300b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203a00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c111e00340078010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010221710221710221610221610221601065010250101000007c8078c06ff0000000000004f80006d0300280307030990098db00b90880991858e08c1810b9190910ac1b8928a0993878a890b9388898908c88191890ac8889289099a818a890b9a88898908d08191890ad08892890802818a095a810a0288890b5a8808d98189890ad9908989095e8289890b5e88898908e18189890ae190898909648289890b648889096e8108e981890b6e880ae99091b9096f828a8f0b6f88918908f0818a890af090898909768289910b76b8918a08f88792910af8888a92097c81898a0b7c09018089890b01888991097f8189920b7f09088089920b088889920c07030307200402000000002f0004007000000029000400700000003500040080000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0x8f3,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032006000000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20700070207000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002a0030000330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c64200050630c642000606c0c64200070620c642000806b0c64200090610c642000a06a0c642000b0600c642000c0690c642000d05f0c642000e05e0c642000f04f0c642000005d0c642000104e0c642000205c0c642000304d0c642000405b0c642000504c0c642000605a0c642000704b0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d10c642000d02b0c642000e0d20c642000f02a0c64200000d30c64200010290c64200020d60c64200030280c64200040d70c642000501d0c64200060d80c642000701c0c64200080d90c642000901b0c642000a0da0c642000b01a0c642000c0de0c642000d0190c642000e0df0c642000f0150c64200000e00c64200010140c64200020e10c64200030130c64200040e20c64200050120c64200060e40c64200070110c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b070100002403089808a9089908a8089a08a7089b08a6089c08a4089d08a3089e08a2089f08a1c0102c03088808b9088908b8088a08b7088b08b3089208b2089308b1089408b0089508afc01003087108c6087208c5087d08c2087e08c1087f08c0088008bf088108bb088708bac0100628070103084f085e085f08690860086a0861086b0862086c086308c9086f08c8087008c7c010030847082e0848082f08490830084a0831084b085a084c085b084d085c084e085dc01003082a08d2082b08d1082c08ce083808cd083908cc083a08cb083b08ca0846082dc0100632070103081508df081908de081a08da081b08d9081c08d8081d08d7082808d6082908d3c01059940b03080a08e8080b08e7080c08e6080d08e5081108e4081208e2081308e1081408e0c0102403080008f1080108f0080208ef080308ee080408ed080508ec080608eb080708eac0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0x8f6,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20500070205000742000007820000084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c64200050630c642000606c0c64200070620c642000806b0c64200090610c642000a06a0c642000b0600c642000c0690c642000d05f0c642000e05e0c642000f04f0c642000005d0c642000104e0c642000205c0c642000304d0c642000405b0c642000504c0c642000605a0c642000704b0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c0000000080681101006411260034001c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b070100000720240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c90863086c0862086b0861086a08600869085f085e084fc01003085d084e085c084d085b084c085a084b0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c0100308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c010070320030c07030703072004020000004200080002089000000000002f00040038000000',
'2f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0x121,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20720070207200742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a120100000620102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063007010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0x121,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20720070207200742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a120100000620102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063007010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0xb4b,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20660070206600742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0xb4b,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20660070206600742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d00005e00004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0xb4b,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050201f015c200000602000004c2003006c20720070207200742004007820040084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d03005e03004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0xb4b,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050201f015c200000602000004c2003006c20720070207200742004007820040084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d03005e03004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x14,
build=0x0,
u1=0x0,
dev_type=0xb4d,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20600070206000742002007820020084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d01005e01004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0xb4d,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20600070206000742002007820020084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'330064030000008064200000a10c642000109f0c64200020a20c642000309e0c64200040a30c642000509d0c64200060a40c642000709c0c64200080a60c642000909b0c642000a0a70c642000b09a0c642000c0a80c642000d0990c642000e0a90c642000f0980c64200000af0c64200010950c64200020b00c64200030940c64200040b10c64200050930c64200060b20c64200070920c64200080b30c642000908b0c642000a0b70c642000b08a0c642000c0b80c642000d0890c642000e0b90c642000f0880c64200000ba0c64200010870c64200020bb0c64200030810c64200040bf0c64200050800c64200060c00c642000707f0c64200080c10c642000907e0c642000a0c20c642000b07d0c642000c0c50c642000d0720c642000e0c60c642000f0710c64200000c70c64200010700c64200020c80c642000306f0c64200040c90c642000506d0c642000607a0c642000706c0c64200080790c642000906b0c642000a0780c642000b06a0c642000c06e0c642000d0690c642000e0630c642000f05e0c64200000620c642000105d0c64200020610c642000305c0c64200040600c642000505b0c642000605f0c642000705a0c64200080310c642000904a0c642000a0300c642000b0490c642000c02f0c642000d0480c642000e02e0c642000f0470c642000002d0c64200010460c64200020ca0c642000303b0c64200040cb0c642000503a0c64200060cc0c64200070390c64200080cd0c64200090380c642000a0ce0c642000b02c0c642000c0d00c642000d02b0c642000e0d10c642000f02a0c64200000d20c64200010290c64200020d30c64200030280c64200040d60c642000501c0c64200060d70c642000701b0c64200080d80c642000901a0c642000a0d90c642000b0190c642000c0da0c642000d0150c642000e0df0c642000f0140c64200000e00c64200010130c64200020e10c64200030120c64200040e20c64200050110c64200060e40c642000700e0c64200080e50c642000900d0c642000a0e60c642000b00c0c642000c0e70c642000d00b0c642000e0e80c642000f00a0c64200000ea0c64200010070c64200020eb0c64200030060c64200040ec0c64200050050c64200060ed0c64200070040c64200080ee0c64200090030c642000a0ef0c642000b0020c642000c0f00c642000d0010c642000e0f10c642000f0000c',
'32000c000000008068110100641126003400180200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a13102a13102a12010000061e102d0101000007c8078c060b5d01005e01004f80005080006d030059940b07010000240308a1089f08a2089e08a3089d08a4089c08a6089b08a7089a08a8089908a90898c0102c0308af089508b0089408b1089308b2089208b3088b08b7088a08b8088908b90888c0100308ba088708bb088108bf088008c0087f08c1087e08c2087d08c5087208c60871c010062807010308c7087008c8086f08c9086d087a086c0879086b0878086a086e08690863085ec010030862085d0861085c0860085b085f085a0831084a08300849082f0848082e0847c01003082d084608ca083b08cb083a08cc083908cd083808ce082c08d0082b08d1082ac010063207010308d2082908d3082808d6081c08d7081b08d8081a08d9081908da081508df0814c01059940b0308e0081308e1081208e2081108e4080e08e5080d08e6080c08e7080b08e8080ac010240308ea080708eb080608ec080508ed080408ee080308ef080208f0080108f10800c0100c0720070c2003040200004200080002089000000000002f00040038000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0x130,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20400070204000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003',
'330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c',
'32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0x518,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20640070206400742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003',
'330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c',
'32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0xbe1,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20550070205500742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003',
'330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c',
'32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x15,
build=0x0,
u1=0x0,
dev_type=0xbe2,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c00000000802020040024200000382000002820df0b2c20df0b302000003420000050200a015c200000602000004c2003006c20500070205000742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a003200b4020000008074030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030001740300037403000174030003740300017403000374030005740300077403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030001740300037403001574030017740300017403000374030015740300177403000174030003740300157403001774030015740300177403000174030003',
'330064030000008064200000e40c64200010050c64200020e60c64200030040c64200040e70c64200050020c64200060eb0c64200070000c64200080ec0c64200090010c642000a0ed0c642000b0030c642000c0f10c642000d0060c642000e0f00c642000f0070c64200000ef0c64200010080c64200020ee0c642000300a0c64200040ea0c642000500b0c64200060e80c642000700c0c64200080e50c642000900d0c642000a0e10c642000b0100c642000c0e00c642000d0140c642000e0df0c642000f0160c64200000de0c64200010190c64200020dc0c642000301d0c64200040db0c642000501e0c64200060da0c64200070220c64200080d30c64200090250c642000a0d10c642000b0270c642000c0cb0c642000d02b0c642000e0ca0c642000f02d0c642000003a0c64200010310c642000203b0c64200030320c642000403d0c64200050370c642000603e0c64200070390c64200080460c642000903c0c642000a0470c642000b03f0c642000c0490c642000d0400c642000e04a0c642000f0450c642000004c0c64200010480c642000204d0c642000304b0c642000405c0c642000504e0c642000605d0c642000704f0c642000805e0c64200090500c642000a0610c642000b0550c642000c0620c642000d05a0c642000e0630c642000f05b0c64200000680c642000105f0c64200020690c64200030600c642000406a0c64200050640c642000606d0c64200070650c642000806e0c642000906b0c642000a06f0c642000b06c0c642000c0700c642000d0710c642000e0c90c642000f0720c64200000c80c64200010770c64200020c60c64200030780c64200040c20c642000507c0c64200060c10c642000707f0c64200080c00c64200090820c642000a0bd0c642000b0840c642000c0bc0c642000d0870c642000e0b80c642000f08b0c64200000b50c642000108f0c64200020b20c64200030910c64200040af0c64200050940c64200060ae0c64200070980c64200080ad0c642000909a0c642000a0ac0c642000b09e0c642000c0ab0c642000d09f0c642000e0a90c642000f09d0c64200000a80c642000109c0c64200020a70c642000309b0c64200040a60c64200050990c64200060a40c64200070960c64200080a30c64200090950c642000a0a20c642000b0930c642000c0a10c642000d0920c642000e0a90c642000f09d0c',
'32000c0000000080681101006411260034000c0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e007e0102a0d102a0d102a0e0100000628102c0101000007c8078c061400004f80005080006d030059940b07010000240308e4080508e6080408e7080208eb080008ec080108ed080308f1080608f00807c0102c0308ef080808ee080a08ea080b08e8080c08e5080d08e1081008e0081408df0816c0100308de081908dc081d08db081e08da082208d3082508d1082708cb082b08ca082dc01003083a0831083b0832083d0837083e08390846083c0847083f08490840084a0845c01003084c0848084d084b085c084e085d084f085e0850086108550862085a0863085bc010030868085f08690860086a0864086d0865086e086b086f086c0870087108c90872c0100308c8087708c6087808c2087c08c1087f08c0088208bd088408bc088708b8088bc0100308b5088f08b2089108af089408ae089808ad089a08ac089e08ab089f08a9089dc010240308a9089d08a8089c08a7089b08a6089908a4089608a3089508a2089308a10892c0100c072c20030703070304020000004200080002089000000000002f00040028000000290004009000000035000400a0000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xb,
build=0x0,
u1=0x0,
dev_type=0x194,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032005c0000000080202004002820ff0b2c20f60b6c202f007020000074201000782000005c2000006020000064200000502000005420151d84202000942001809c200902a0200b19b4200000b8203a00bc201400c0200200c4200200c82008003300100000000080cc2000004900d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0201400f8200500fc200000b8203b00002804001428000008280000082810001428180008281100142819001c281a00',
'32001400000000806811010064111f007c1700007017010033001c00000000809c1701000000ac1700900100b01754000000b417007200003400500010061110061110061110061110061001068010080101000007c80764060000002003070107010c07840a640863c91e2cc9159107010a9dc9110701030a3208330701c90f20c91e070304020000000000',
'2f0004005400000029000400000000003500040010000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0x179,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032007800000000802020040024200000382000002820df0b2c20df0b30200000342000003c20000040200000442000004820000050200a005c20000060200000642000004c200300002100006c2030007020300040210000742006007820060084202000b4200000b8203a00bc201400c0200200c4200100c820020033001c000000008054202f27030058202f270300cc200000ef03d0200000ef033200440000000080dc20e803e020e803e420d002e820d002f0200500f8200500fc200000b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'320014000000008068110100641126007c1700007017010033001c00000000809c1701000000ac1700900100b01738000000b417009000003400040310060b10060b10060b10060b10060c01064e10080101000007c80764060000002003070107010c2c078408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b800aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a720871800ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a800ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a060805800aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800a010800802007032003070307030402000000',
'2f0004003800000029000400000000003500040010000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xa,
build=0x0,
u1=0x0,
dev_type=0x117,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032006400000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c20680070206800742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a80063207010ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a06080580240aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800c0a01080080070103040200000000002f00040038000000',
'290004009400000035000400a4000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xb,
build=0x0,
u1=0x0,
dev_type=0x117,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032006400000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c20680070206800742001007820010084202000b4200000bc201400c0200200c4200100c820020074030002330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d1800a2c082b800ad108d2800a2b082a80063207010ad208d3800a2a0829800ad308d6800a290828800ad608d7800a28081d800ad708d8800a1d081c800ad808d9800a1c081b800ad908da800a1b081a800ada08de800a1a0819800ade08df800a190815800adf08e0800a150814800ae008e1800a140813800ae108e2800a130812800ae208e4800a120811800ae408e5800a11080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708ea800a0b080a800aea08eb800a0a0807800aeb08ec800a070806800aec08ed800a06080580240aed08ee800a050804800aee08ef800a040803800aef08f0800a030802800af008f1800a020801800af108f2800c0a01080080070103040200000000002f00040038000000',
'290004009400000035000400a4000000'
]),
SensorCaptureProg(
major=0x6,
minor=0xb,
build=0x0,
u1=0x0,
dev_type=0x126,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032007000000000802020040024200000382000002820df0b2c20df0b30200000342000003c2080004020800050200a005c200000602000004c2003006c2068007020680074200100782001006420000084202000b4200000b8203a00bc201400c0200200c4200100c820020074030000a0030f00330028000000008054202a2203005820272f03004420898103004820868e0300cc200000ef03d0200000ef033200400000000080dc20fa00e020fa00e420b400e820b400f0200500f8200500b8203a00002804001428000008280000082800001428300008280000142831001c281a00',
'32000c000000008068110100641126003400ac0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102b0e102b0e102b0e102b0e01000000061e10301001000007c80764060b4f80005080006d030007010000006d0300200307010007032408a180089f800aa108a2800a9f089e800aa208a3800a9e089d800aa308a4800a9d089c800aa408a6800a9c089b802c0aa608a7800a9b089a800aa708a8800a9a0899800aa808a9800a990898800aa908af800a980895800aaf08b0800a950894800ab008b1800a940893800ab108b2800a930892800ab208b3800a92088b800ab308b7800a8b088a800ab708b8800a8a0889800ab808b9800a890888800ab908ba800a880887800aba08bb800a870881800abb08bf800a810880800abf08c0800a80087f800ac008c1800a7f087e800ac108c2800a7e087d800ac208c5800a7d0872800ac508c6800a72087180062807010ac608c7800a710870800ac708c8800a70086f800ac808c9800a6f0863800ac9086c800a630862800a6c086b800a620861800a6b086a800a610860800a6a0869800a60085f800a69085e800a5f084f800a5e085d800a4f084e800a5d085c800a4e084d800a5c085b800a4d084c800a5b085a800a4c084b800a5a0831800a4b084a800a310830800a4a0849800a30082f800a490848800a2f082e800a480847800a2e082d800a470846800a2d08ca800a46083b800aca08cb800a3b083a800acb08cc800a3a0839800acc08cd800a390838800acd08ce800a38082c800ace08d0800a2c082b800ad008d1800a2b082a80063207010ad108d2800a2a0829800ad208d3800a290828800ad308d6800a28081c800ad608d7800a1c081b800ad708d8800a1b081a800ad808d9800a1a0819800ad908da800a190815800ada08df800a150814800adf08e0800a140813800ae008e1800a130812800ae108e2800a120811800ae208e4800a11080e800ae408e5800a0e080d800ae508e6800a0d080c800ae608e7800a0c080b800ae708e8800a0b080a800ae808ea800a0a0807800aea08eb800a070806800aeb08ec800a06080580240aec08ed800a050804800aed08ee800a040803800aee08ef800a030802800aef08f0800a020801800af008f1800c0a01080080070c03040200000000002f00040038000000290004009400000035000400a4000000'
]),
SensorCaptureProg(
major=0x6,
minor=0x0,
build=0x0,
u1=0x0,
dev_type=0xdb,
a0=0x18,
a1=0x19,
blobs=[
'2300000020000800002000800000010032007000000000802020050024200000502077362820010030200100082170000c210000482102004c210000582000005c20000060200000682005006c20012970200121742001887820018084202000942001809c200902a0200b19b4200000b8203b04bc201400c0200200c4200100c82002003300100000000080cc200000f503d0200000a1013200440000000080dc20e803e0206401e420d002e8200001f0200500f8200500fc200000b8203b00000804001408000008080000080800001408300008080000140831001c081a00',
'32000c0000000080501101004c1126003400080310061d10061d10061d10061d10061c01065810080101000007c8078c06100000204f80007f000003070107010c07032c08fc80095a800afc08fb800b5a095b800afb08fa800b5b095c800afa08f9800b5c095d800af908f8800b5d095e800af808f7800b5e095f800af708f6800b5f0960800af608f5800b600961800af508f4800b610962800af408f3800b620963800af308f2800b630964800af208f1800b640965800af108f0800b650966800af008ef800b660967800aef08ee800b670968800aee08ed800b68096c800aed08ec800b6c096d800aec08eb800b6d096e800aeb08ea800b6e096f800aea08e9800b6f0970800ae908e8800b700971800ae808e7800b710972800ae708e6800b720973800ae608e5800b730974800ae508e4800b740975800ae408e3800b750976800ae308e2800b760977800ae208e1800b770978800ae108e0800b780979800ae008df800b79097a800adf08de800b7a097b800ade08dd800b7b097c800add08dc800b7c097d800adc08db800b7d097e800adb08da800b7e097f800ada08d9800b7f0980800ad908d8800b800981800ad808d7800b810982800ad708d6800b820983800ad608d5800b830984800ad508d4800b840985800ad408d3800b850986800ad308d2800b860987800ad208d1800b870988800ad108d0800b880989800ad008cf800b89098a800acf08ce800b8a098b800ace08cd800b8b098c800acd08cc800b8c098d800acc08cb800b8d098e800acb08ca800b8e098f800aca08c9800b8f0990800ac908c8800b900991800ac808c7800b910992800ac708c6800b920993800ac608c5800b930994800ac508c4800b940995800ac408c3800b950996800ac308c2800b960997800ac208c1800b970998800ac108c0800b980999800ac008bf800b99099a800abf08be800b9a099b800abe08bd800b9b099c800abd08bc800b9c099d800abc08bb800b9d099e800abb08ba800b9e099f800aba08b9800b9f09a0800ab908b8800ba00801800ab808b7800a010802800ab708b6800a020803800ab608b5800a030804802003070404020000000000002f0004009000000029000400000000003500040010000000'
]),
] ]

View file

@ -1,14 +1,13 @@
class DeviceInfo:
class DeviceInfo(): def __init__(self, major: int, type: int, version: int, version_mask: int, name: str):
def __init__(self, major, type, version, version_mask, name):
self.major, self.type, self.version, self.version_mask, self.name = major, type, version, version_mask, name self.major, self.type, self.version, self.version_mask, self.name = major, type, version, version_mask, name
def __repr__(self): def __repr__(self):
return "DeviceInfo(0x%04x, 0x%04x, 0x%02x, 0x%02x, '%s')" % ( return "DeviceInfo(0x%04x, 0x%04x, 0x%02x, 0x%02x, '%s')" % (
self.major, self.type, self.version, self.version_mask, self.name self.major, self.type, self.version, self.version_mask, self.name)
)
dev_info_table=[
dev_info_table = [
DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '), DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '),
DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '), DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '),
DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '), DeviceInfo(0x0000, 0x0080, 0x00, 0x00, 'VSI 15A '),
@ -428,7 +427,8 @@ dev_info_table=[
DeviceInfo(0x0190, 0x2449, 0xa3, 0xff, '86C TM-P3569-001 '), DeviceInfo(0x0190, 0x2449, 0xa3, 0xff, '86C TM-P3569-001 '),
] ]
def dev_info_lookup(major, ver):
def dev_info_lookup(major: int, ver: int):
fuzzy_match = None fuzzy_match = None
for i in dev_info_table: for i in dev_info_table:
@ -444,16 +444,19 @@ def dev_info_lookup(major, ver):
return fuzzy_match return fuzzy_match
class FlashIcInfo():
def __init__(self, name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd, f25, f26): class FlashIcInfo:
def __init__(self, name: str, size: int, f18: int, jid0: int, jid1: int, f1b: int, f1c: int,
f1e: int, secror_size: int, sector_erase_cmd: int, f25: int, f26: int):
self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e, self.secror_size, self.sector_erase_cmd, self.f25, self.f26 = name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd, f25, f26 self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e, self.secror_size, self.sector_erase_cmd, self.f25, self.f26 = name, size, f18, jid0, jid1, f1b, f1c, f1e, secror_size, sector_erase_cmd, f25, f26
def __repr__(self): def __repr__(self):
return "FlashIcInfo('%s', %d, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)" % ( return "FlashIcInfo('%s', %d, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)" % (
self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e, self.secror_size, self.sector_erase_cmd, self.f25, self.f26) self.name, self.size, self.f18, self.jid0, self.jid1, self.f1b, self.f1c, self.f1e,
self.secror_size, self.sector_erase_cmd, self.f25, self.f26)
flash_ic_table=[
flash_ic_table = [
FlashIcInfo('M25P05-A', 65536, 0x5, 0x20, 0x20, 0x10, 0x8000, 0x0, 0x8000, 0xd8, 0x0, 0x4), FlashIcInfo('M25P05-A', 65536, 0x5, 0x20, 0x20, 0x10, 0x8000, 0x0, 0x8000, 0xd8, 0x0, 0x4),
FlashIcInfo('M25P10-A', 131072, 0x10, 0x20, 0x20, 0x11, 0x8000, 0x0, 0x8000, 0xd8, 0x0, 0x4), FlashIcInfo('M25P10-A', 131072, 0x10, 0x20, 0x20, 0x11, 0x8000, 0x0, 0x8000, 0xd8, 0x0, 0x4),
FlashIcInfo('M25P20', 262144, 0x11, 0x20, 0x20, 0x12, 0x0, 0x1, 0x10000, 0xd8, 0x0, 0x4), FlashIcInfo('M25P20', 262144, 0x11, 0x20, 0x20, 0x12, 0x0, 0x1, 0x10000, 0xd8, 0x0, 0x4),
@ -476,10 +479,10 @@ flash_ic_table=[
FlashIcInfo('GD25Q80C', 1048576, 0x13, 0xc8, 0x40, 0x14, 0x0, 0x1, 0x1000, 0x20, 0x0, 0x4), FlashIcInfo('GD25Q80C', 1048576, 0x13, 0xc8, 0x40, 0x14, 0x0, 0x1, 0x1000, 0x20, 0x0, 0x4),
] ]
def flash_ic_table_lookup(jedec_id0, jedec_id1, size):
def flash_ic_table_lookup(jedec_id0: int, jedec_id1: int, size: int):
for i in flash_ic_table: for i in flash_ic_table:
if i.jid0 == jedec_id0 and i.jid1 == jedec_id1 and i.size == size: if i.jid0 == jedec_id0 and i.jid1 == jedec_id1 and i.size == size:
return i return i
return None return None

View file

@ -1,14 +1,14 @@
import atexit import atexit
import logging import logging
from validitysensor.usb import usb
from validitysensor.tls import tls
from validitysensor.sensor import sensor, reboot, RebootException
from validitysensor.flash import read_tls_flash from validitysensor.flash import read_tls_flash
from validitysensor.init_flash import init_flash
from validitysensor.upload_fwext import upload_fwext
from validitysensor.init_db import init_db from validitysensor.init_db import init_db
from validitysensor.init_flash import init_flash
from validitysensor.sensor import sensor, reboot, RebootException
from validitysensor.tls import tls
from validitysensor.upload_fwext import upload_fwext
from validitysensor.usb import usb
def close(): def close():
logging.debug('In atexit handler') logging.debug('In atexit handler')
@ -24,16 +24,17 @@ def close():
finally: finally:
usb.close() usb.close()
def open_common(): def open_common():
init_flash() init_flash()
usb.send_init() usb.send_init()
tls.parseTlsFlash(read_tls_flash()) tls.parse_tls_flash(read_tls_flash())
tls.open() tls.open()
upload_fwext() upload_fwext()
sensor.open() sensor.open()
init_db() init_db()
# We must register atexit only after we opened usb device, # We must register atexit only after we opened usb device,
# so that our atexit handler is called before pyusb's one and we can still talk to the device # so that our atexit handler is called before pyusb's one and we can still talk to the device
# #
# Don't attempt to autoreboot unless we finished initializing the sensor successfully. # Don't attempt to autoreboot unless we finished initializing the sensor successfully.
@ -41,10 +42,12 @@ def open_common():
atexit.register(close) atexit.register(close)
logging.debug('atexit registered') logging.debug('atexit registered')
def open(): def open():
usb.open() usb.open()
open_common() open_common()
def open_devpath(busnum, address):
def open_devpath(busnum: int, address: int):
usb.open_devpath(busnum, address) usb.open_devpath(busnum, address)
open_common() open_common()

View file

@ -1,13 +1,14 @@
from struct import pack, unpack
import logging import logging
from struct import pack, unpack
from .db import db from .db import db
def machine_id_rec_value(b):
def machine_id_rec_value(b: str):
b = b.encode('utf-16le') b = b.encode('utf-16le')
b = b + b'\0' * (0x94 - len(b)) b = b + b'\0' * (0x94 - len(b))
return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID? return pack('<HH', 0x102, len(b)) + b # 0x102 = Machine ID/GUID?
# This function is unused. It simply describes the Windows driver behavior. # This function is unused. It simply describes the Windows driver behavior.
# TODO: make this GUID unique! # TODO: make this GUID unique!
@ -24,15 +25,15 @@ def init_machine_guid(machine_guid='e7260876-58db-4d27-8c40-8d13110d6a71'):
if rc != machine_id_rec_value(machine_guid): if rc != machine_id_rec_value(machine_guid):
u0, l = unpack('<HH', rc[:4]) u0, l = unpack('<HH', rc[:4])
b=rc[4:4+l] b = rc[4:4 + l]
b=b.decode('utf-16le') b = b.decode('utf-16le')
raise Exception('Machine GUID does not match the DB flash ownership record (%s).' % b) raise Exception('Machine GUID does not match the DB flash ownership record (%s).' % b)
def init_db(): def init_db():
stg = db.get_user_storage(name='StgWindsor') stg = db.get_user_storage(name='StgWindsor')
if stg == None: if stg is None:
logging.info('Creating a new user storage object') logging.info('Creating a new user storage object')
db.new_user_storate() db.new_user_storate()
#init_machine_guid() # init_machine_guid()

View file

@ -1,61 +1,64 @@
import os
from struct import pack, unpack
from binascii import unhexlify
import logging
from hashlib import sha256
import hmac import hmac
import logging
import os
import typing
from binascii import unhexlify
from hashlib import sha256
from struct import pack, unpack
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from .blobs import reset_blob
from .flash import write_flash, erase_flash, call_cleanups, PartitionInfo, get_flash_info, FlashInfo
from .hw_tables import FlashIcInfo
from .sensor import reboot, RomInfo
from .tls import tls, hs_key, crt_hardcoded from .tls import tls, hs_key, crt_hardcoded
from .usb import usb from .usb import usb
from .flash import write_flash, erase_flash, call_cleanups, PartitionInfo, get_flash_info
from .sensor import reboot, RomInfo
from .util import assert_status, unhex from .util import assert_status, unhex
from .blobs import reset_blob
flash_layout_hardcoded=[ flash_layout_hardcoded = [
# id type access offset size # id type access offset size
# lvl # lvl
PartitionInfo(1, 4, 7, 0x00001000, 0x00001000), # cert store PartitionInfo(1, 4, 7, 0x00001000, 0x00001000), # cert store
PartitionInfo(2, 1, 2, 0x00002000, 0x0003e000), # xpfwext PartitionInfo(2, 1, 2, 0x00002000, 0x0003e000), # xpfwext
PartitionInfo(5, 5, 3, 0x00040000, 0x00008000), # ??? PartitionInfo(5, 5, 3, 0x00040000, 0x00008000), # ???
PartitionInfo(6, 6, 3, 0x00048000, 0x00008000), # calibration data PartitionInfo(6, 6, 3, 0x00048000, 0x00008000), # calibration data
PartitionInfo(4, 3, 5, 0x00050000, 0x00080000), # template database PartitionInfo(4, 3, 5, 0x00050000, 0x00080000), # template database
] ]
partition_signature=unhex(''' partition_signature = unhex('''
1db02a886b007e2b47263bb8fe30bd64a1f58bea7b25f1e1ba9ae09add7ecff36333f8198339cdd713f043633710a17bc7b3f418f1d8ff435a1bf47f065dffca 1db02a886b007e2b47263bb8fe30bd64a1f58bea7b25f1e1ba9ae09add7ecff36333f8198339cdd713f043633710a17bc7b3f418f1d8ff435a1bf47f065dffca
727109152217fce73bf2bf8e01a1641f6a24b0c492a6a3f10114057275846842b1c8b66bd6700738524d4471bca3315ba23bb832743220ad195b60558aa79a3e 727109152217fce73bf2bf8e01a1641f6a24b0c492a6a3f10114057275846842b1c8b66bd6700738524d4471bca3315ba23bb832743220ad195b60558aa79a3e
deb2604834e2bb62e890b0ce405b3b8ef2fec2aab3e22bff23f89a58ff0dc015fece5d3ed3f5496ace879a92980aec9d85eb7e9df245eae03a41acfd4e7d1cb1 deb2604834e2bb62e890b0ce405b3b8ef2fec2aab3e22bff23f89a58ff0dc015fece5d3ed3f5496ace879a92980aec9d85eb7e9df245eae03a41acfd4e7d1cb1
dbd0df42d534904de00b6389f68867646e9d7c3d0b1dffd74070b2d0f2049b9f1dc7b0c9651c59be3ea891674725e1f2f7a484a941615b80211105978369cf71 dbd0df42d534904de00b6389f68867646e9d7c3d0b1dffd74070b2d0f2049b9f1dc7b0c9651c59be3ea891674725e1f2f7a484a941615b80211105978369cf71
''') ''')
crypto_backend=default_backend() crypto_backend = default_backend()
def get_partition_signature(): def get_partition_signature():
if usb.usb_dev().idVendor == 0x138a: if usb.usb_dev().idVendor == 0x138a:
if usb.usb_dev().idProduct == 0x0090: if usb.usb_dev().idProduct == 0x0090:
return b'' return b''
return partition_signature return partition_signature
def with_hdr(id, buf): def with_hdr(id: int, buf: bytes):
return pack('<HH', id, len(buf)) + buf return pack('<HH', id, len(buf)) + buf
def encrypt_key(client_private, client_public): def encrypt_key(client_private, client_public):
x = unhexlify('%064x' % client_public.x)[::-1] x = unhexlify('%064x' % client_public.x)[::-1]
y = unhexlify('%064x' % client_public.y)[::-1] y = unhexlify('%064x' % client_public.y)[::-1]
d = unhexlify('%064x' % client_private)[::-1] d = unhexlify('%064x' % client_private)[::-1]
m = x+y+d m = x + y + d
l = 16 - (len(m) % 16) l = 16 - (len(m) % 16)
m = m + bytes([l])*l m = m + bytes([l]) * l
iv = os.urandom(0x10) iv = os.urandom(0x10)
cipher = Cipher(algorithms.AES(tls.psk_encryption_key), modes.CBC(iv), backend=crypto_backend) cipher = Cipher(algorithms.AES(tls.psk_encryption_key), modes.CBC(iv), backend=crypto_backend)
@ -64,45 +67,48 @@ def encrypt_key(client_private, client_public):
sig = hmac.new(tls.psk_validation_key, c, sha256).digest() sig = hmac.new(tls.psk_validation_key, c, sha256).digest()
return b'\x02' + c + sig return b'\x02' + c + sig
def make_cert(client_public): def make_cert(client_public):
msg=(pack('<LL', 0x17, 0x20) + msg = (pack('<LL', 0x17, 0x20) + unhexlify('%064x' % client_public.x)[::-1] + (b'\0' * 0x24) +
unhexlify('%064x' % client_public.x)[::-1] + unhexlify('%064x' % client_public.y)[::-1] + (b'\0' * 0x4c))
(b'\0'*0x24) +
unhexlify('%064x' % client_public.y)[::-1] +
(b'\0'*0x4c))
pk = ec.derive_private_key(hs_key(), ec.SECP256R1(), backend=crypto_backend) pk = ec.derive_private_key(hs_key(), ec.SECP256R1(), backend=crypto_backend)
s=pk.sign(msg, ec.ECDSA(hashes.SHA256())) s = pk.sign(msg, ec.ECDSA(hashes.SHA256()))
s=pack('<L', len(s)) + s s = pack('<L', len(s)) + s
msg = msg + s msg = msg + s
msg += b'\0'*(444 - len(msg)) # FIXME not sure this math is right msg += b'\0' * (444 - len(msg)) # FIXME not sure this math is right
return msg return msg
def serialize_flash_params(ic):
def serialize_flash_params(ic: FlashIcInfo):
return pack('<LLxxBx', ic.size, ic.secror_size, ic.sector_erase_cmd) return pack('<LLxxBx', ic.size, ic.secror_size, ic.sector_erase_cmd)
def serialize_partition(p):
def serialize_partition(p: PartitionInfo):
b = pack('<BBHLL', p.id, p.type, p.access_lvl, p.offset, p.size) b = pack('<BBHLL', p.id, p.type, p.access_lvl, p.offset, p.size)
b = b + b'\0'*4 + sha256(b).digest() b = b + b'\0' * 4 + sha256(b).digest()
return b return b
def partition_flash(info, layout, client_public):
def partition_flash(info: FlashInfo, layout: typing.List[PartitionInfo], client_public):
logging.info('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size)) logging.info('Detected Flash IC: %s, %d bytes' % (info.ic.name, info.ic.size))
cmd = unhex('4f 0000 0000') cmd = unhex('4f 0000 0000')
cmd += with_hdr(0, serialize_flash_params(info.ic)) cmd += with_hdr(0, serialize_flash_params(info.ic))
cmd += with_hdr(1, b''.join([serialize_partition(p) for p in layout]) + get_partition_signature()) cmd += with_hdr(1,
b''.join([serialize_partition(p) for p in layout]) + get_partition_signature())
cmd += with_hdr(5, make_cert(client_public)) cmd += with_hdr(5, make_cert(client_public))
cmd += with_hdr(3, crt_hardcoded) cmd += with_hdr(3, crt_hardcoded)
rsp = tls.cmd(cmd) rsp = tls.cmd(cmd)
assert_status(rsp) assert_status(rsp)
rsp = rsp[2:] rsp = rsp[2:]
crt_len, rsp=rsp[:4], rsp[4:] crt_len, rsp = rsp[:4], rsp[4:]
crt_len, = unpack('<L', crt_len) crt_len, = unpack('<L', crt_len)
tls.handle_cert(rsp[:crt_len]) tls.handle_cert(rsp[:crt_len])
rsp = rsp[crt_len:] rsp = rsp[crt_len:]
# ^ TODO - figure out what the rest of rsp means # ^ TODO - figure out what the rest of rsp means
def init_flash(): def init_flash():
info = get_flash_info() info = get_flash_info()
@ -125,13 +131,13 @@ def init_flash():
# ^ TODO: use the firmware version which to lookup pubkey for server cert validation # ^ TODO: use the firmware version which to lookup pubkey for server cert validation
try: try:
rsp=usb.cmd(unhex('50')) rsp = usb.cmd(unhex('50'))
assert_status(rsp) assert_status(rsp)
finally: finally:
call_cleanups() call_cleanups()
rsp=rsp[2:] rsp = rsp[2:]
l,=unpack('<L', rsp[:4]) l, = unpack('<L', rsp[:4])
if len(rsp) != l: if len(rsp) != l:
raise Exception('Length mismatch') raise Exception('Length mismatch')
@ -153,8 +159,8 @@ def init_flash():
erase_flash(4) erase_flash(4)
# Persist certs and keys on cert partition. # Persist certs and keys on cert partition.
write_flash(1, 0, tls.makeTlsFlash()) write_flash(1, 0, tls.make_tls_flash())
# Reboot. # Reboot.
# The device will disconnect and our service will be started by udev as soon as it is connected again. # The device will disconnect and our service will be started by udev as soon as it is connected again.
reboot() reboot()

View file

@ -1,83 +1,101 @@
import logging
import os.path
import typing import typing
from binascii import hexlify, unhexlify
from enum import Enum from enum import Enum
from hashlib import sha256 from hashlib import sha256
import os.path from struct import pack, unpack
import logging from time import sleep
from usb import core as usb_core from usb import core as usb_core
from .tls import tls
from .usb import usb, CancelledException from . import timeslot as prg
from .blobs import reset_blob
from .db import db, SidIdentity from .db import db, SidIdentity
from .flash import write_enable, call_cleanups, read_flash, erase_flash, write_flash_all, read_flash_all from .flash import write_enable, call_cleanups, read_flash, erase_flash, write_flash_all, read_flash_all
from time import sleep
from struct import pack, unpack
from .table_types import SensorTypeInfo, SensorCaptureProg
from binascii import hexlify, unhexlify
from .util import assert_status, unhex
from .hw_tables import dev_info_lookup from .hw_tables import dev_info_lookup
from .blobs import reset_blob from .table_types import SensorTypeInfo, SensorCaptureProg
from . import timeslot as prg from .tls import tls
from .usb import usb, CancelledException
from .util import assert_status, unhex
# TODO: this should be specific to an individual device (system may have more than one sensor) # TODO: this should be specific to an individual device (system may have more than one sensor)
calib_data_path='/usr/share/python-validity/calib-data.bin' calib_data_path = '/usr/share/python-validity/calib-data.bin'
line_update_type1_devices = [
0xB5, 0x885, 0xB3, 0x143B, 0x1055, 0xE1, 0x8B1, 0xEA, 0xE4, 0xED, 0x1825, 0x1FF5, 0x199
]
line_update_type1_devices = [ 0xB5, 0x885, 0xB3, 0x143B, 0x1055, 0xE1, 0x8B1, 0xEA, 0xE4, 0xED, 0x1825, 0x1FF5, 0x199 ]
# TODO use more sophisticated glow patters in different cases # TODO use more sophisticated glow patters in different cases
def glow_start_scan(): def glow_start_scan():
cmd=unhexlify('3920bf0200ffff0000019900200000000099990000000000000000000000000020000000000000000000000000ffff000000990020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') cmd = unhexlify(
'3920bf0200ffff0000019900200000000099990000000000000000000000000020000000000000000000000000ffff000000990020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)
assert_status(tls.app(cmd)) assert_status(tls.app(cmd))
def glow_end_scan(): def glow_end_scan():
cmd=unhexlify('39f4010000f401000001ff002000000000ffff0000000000000000000000000020000000000000000000000000f401000000ff0020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') cmd = unhexlify(
'39f4010000f401000001ff002000000000ffff0000000000000000000000000020000000000000000000000000f401000000ff0020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
)
assert_status(tls.app(cmd)) assert_status(tls.app(cmd))
def get_prg_status(): def get_prg_status():
return tls.app(unhexlify('5100000000')) return tls.app(unhexlify('5100000000'))
def wait_till_finished(): def wait_till_finished():
while True: while True:
status = get_prg_status() status = get_prg_status()
if status[0] in [0, 7]: if status[0] in [0, 7]:
break break
sleep(0.2) sleep(0.2)
def get_prg_status2(): def get_prg_status2():
return tls.app(unhexlify('5100200000')) return tls.app(unhexlify('5100200000'))
def read_hw_reg32(addr): def read_hw_reg32(addr: int):
rsp=tls.cmd(pack('<BLB', 7, addr, 4)) rsp = tls.cmd(pack('<BLB', 7, addr, 4))
assert_status(rsp) assert_status(rsp)
rsp, = unpack('<L', rsp[2:]) rsp, = unpack('<L', rsp[2:])
return rsp return rsp
def write_hw_reg32(addr, val):
rsp=tls.cmd(pack('<BLLB', 8, addr, val, 4)) def write_hw_reg32(addr: int, val: int):
rsp = tls.cmd(pack('<BLLB', 8, addr, val, 4))
assert_status(rsp) assert_status(rsp)
class RebootException(Exception): class RebootException(Exception):
pass pass
def reboot(): def reboot():
assert_status(tls.cmd(unhex('050200'))) assert_status(tls.cmd(unhex('050200')))
raise RebootException() raise RebootException()
def factory_reset(): def factory_reset():
assert_status(usb.cmd(reset_blob)) assert_status(usb.cmd(reset_blob))
assert_status(usb.cmd(b'\x10' + b'\0'*0x61)) assert_status(usb.cmd(b'\x10' + b'\0' * 0x61))
reboot() reboot()
class RomInfo():
class RomInfo:
@classmethod @classmethod
def get(cls): def get(cls):
rsp=tls.cmd(b'\x01') rsp = tls.cmd(b'\x01')
assert_status(rsp) assert_status(rsp)
rsp=rsp[2:] rsp = rsp[2:]
return cls(*unpack('<LLBBxBxxxB', rsp[0:0x10])) return cls(*unpack('<LLBBxBxxxB', rsp[0:0x10]))
def __init__(self, timestamp, build, major, minor, product, u1): def __init__(self, timestamp: int, build: int, major: int, minor: int, product: int, u1: int):
self.timestamp, self.build, self.major, self.minor, self.product, self.u1 = timestamp, build, major, minor, product, u1 self.timestamp, self.build, self.major, self.minor, self.product, self.u1 = timestamp, build, major, minor, product, u1
def __repr__(self): def __repr__(self):
@ -86,9 +104,9 @@ class RomInfo():
def identify_sensor(): def identify_sensor():
rsp=tls.cmd(b'\x75') rsp = tls.cmd(b'\x75')
assert_status(rsp) assert_status(rsp)
rsp=rsp[2:] rsp = rsp[2:]
zeroes, minor, major = unpack('<LHH', rsp) zeroes, minor, major = unpack('<LHH', rsp)
@ -97,6 +115,7 @@ def identify_sensor():
return dev_info_lookup(major, minor) return dev_info_lookup(major, minor)
# <<< 0000 880d 0000 07000000 # <<< 0000 880d 0000 07000000
# 08000000 9400 0e00 0300 0080 07000000 7e7f807f808080808080808080808080808080808080818081808180818080808080818081808080818081808180 # 08000000 9400 0e00 0300 0080 07000000 7e7f807f808080808080808080808080808080808080818081808180818080808080818081808080818081808180
# a4000000 0800 0e00 0200 0000 00000000 0d007100 # a4000000 0800 0e00 0200 0000 00000000 0d007100
@ -105,15 +124,15 @@ def identify_sensor():
# d0000000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f # d0000000 9400 0e00 0700 0080 07000000 2b23203c2d182e1e30182e1c321d341d341e321c301e1e241e201f201d1c321a301e1c211e21341f1e202024201f
# 6c010000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107 # 6c010000 1400 0e00 0f00 0080 05550007 7701002805720000080100020811e107
# 88010000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800 # 88010000 0c00 0e00 1200 0080 07000000 7002 7800 7002 7800
def get_factory_bits(tag): def get_factory_bits(tag: int):
rsp=tls.cmd(pack('<B H HL', 0x6f, tag, 0, 0)) rsp = tls.cmd(pack('<B H HL', 0x6f, tag, 0, 0))
assert_status(rsp) assert_status(rsp)
rsp=rsp[2:] rsp = rsp[2:]
wtf, entries = unpack('<LL', rsp[:8]) wtf, entries = unpack('<LL', rsp[:8])
rsp = rsp[8:] rsp = rsp[8:]
rc={} rc = {}
for x in range(0, entries): for x in range(0, entries):
hdr, rsp = rsp[:12], rsp[12:] hdr, rsp = rsp[:12], rsp[12:]
ptr, l, tag, subtag, flags = unpack('<LHHHH', hdr) ptr, l, tag, subtag, flags = unpack('<LHHHH', hdr)
@ -121,7 +140,7 @@ def get_factory_bits(tag):
if len(value) != l: if len(value) != l:
raise Exception('Truncated response %d != %d' % (len(value), l)) raise Exception('Truncated response %d != %d' % (len(value), l))
rc[subtag] = value rc[subtag] = value
rsp = rsp[l:] rsp = rsp[l:]
@ -132,95 +151,106 @@ def get_factory_bits(tag):
def bitpack(b): def bitpack(b):
l=len(b) l = len(b)
m=min(b) m = min(b)
x=max(b) x = max(b)
# maximum delta which we must encode # maximum delta which we must encode
x-=m x -= m
# count useful bits # count useful bits
u=0 u = 0
while x > 0: while x > 0:
x>>=1 x >>= 1
u+=1 u += 1
# convert to array of binary strings with each element exactly u characters long # convert to array of binary strings with each element exactly u characters long
b=[bin(i-m+0x100)[-u:] for i in b] b = [bin(i - m + 0x100)[-u:] for i in b]
# combine chunks into one long text number with u*l binary digits and parse it as integer # combine chunks into one long text number with u*l binary digits and parse it as integer
b=int(''.join(b[::-1]), 2) b = int(''.join(b[::-1]), 2)
# convert back to bytes # convert back to bytes
b=b.to_bytes((u*l+7)//8, 'little') b = b.to_bytes((u * l + 7) // 8, 'little')
return (u, m, b) return u, m, b
class Line():
mask=None
flags=None
data=None
v0=0
v1=0
v2=0
def clip(x): class Line:
def __init__(self):
self.mask: typing.Optional[int] = None
self.flags: typing.Optional[int] = None
self.data: typing.Optional[bytes] = None
self.v0 = 0
self.v1 = 0
self.v2 = 0
def clip(x: int):
if x < -128: if x < -128:
x=-128 x = -128
if x > 127: if x > 127:
x=127 x = 127
return x & 0xff return x & 0xff
def scale(x):
def scale(x: int):
x -= 0x80 x -= 0x80
x = int(x*10/0x22) # TODO: scaling factor depends on a device x = int(x * 10 / 0x22) # TODO: scaling factor depends on a device
return clip(x) return clip(x)
def add(l, r): def add(l: int, r: int):
# Make signed # Make signed
l, r = unpack('bb', pack('BB', l, r)) l, r = unpack('bb', pack('BB', l, r))
return clip(l+r) return clip(l + r)
def chunks(b: bytes, l: int):
return [b[i:i + l] for i in range(0, len(b), l)]
def chunks(b, l):
return [b[i:i+l] for i in range(0, len(b), l)]
class CaptureMode(Enum): class CaptureMode(Enum):
CALIBRATE=1 CALIBRATE = 1
IDENTIFY=2 IDENTIFY = 2
ENROLL=3 ENROLL = 3
class Sensor():
calib_data=b'' class Sensor:
calib_data = b''
def open(self): def open(self):
self.device_info = identify_sensor() self.device_info = identify_sensor()
logging.info('Opening sensor: %s' % self.device_info.name) logging.info('Opening sensor: %s' % self.device_info.name)
self.type_info = SensorTypeInfo.get_by_type(self.device_info.type) self.type_info = SensorTypeInfo.get_by_type(self.device_info.type)
if self.device_info.type == 0x199: if self.device_info.type == 0x199:
self.key_calibration_line = 0x38 # (lines_per_calibration_data/2), but hardcoded for sensor type 0x199 self.key_calibration_line = 0x38 # (lines_per_calibration_data/2), but hardcoded for sensor type 0x199
self.calibration_frames = 3 # TODO: workout where it's really comming from self.calibration_frames = 3 # TODO: workout where it's really comming from
self.calibration_iterations = 3 # hardcoded for type self.calibration_iterations = 3 # hardcoded for type
elif self.device_info.type == 0xdb: elif self.device_info.type == 0xdb:
self.key_calibration_line = 0x48 # TODO 48 is just a guess -- find it self.key_calibration_line = 0x48 # TODO 48 is just a guess -- find it
self.calibration_frames = 6 # TODO: workout where it's really comming from self.calibration_frames = 6 # TODO: workout where it's really comming from
self.calibration_iterations = 0 self.calibration_iterations = 0
else: else:
raise Exception('Device %s is not supported (sensor type 0x%x)' % (self.device_info.name, self.device_info.type)) raise Exception('Device %s is not supported (sensor type 0x%x)' %
(self.device_info.name, self.device_info.type))
self.rom_info = RomInfo.get() self.rom_info = RomInfo.get()
self.hardcoded_prog = SensorCaptureProg.get(self.rom_info, self.device_info.type, 0x18, 0x19) # TODO: find where 0x18, 0x19 coming from self.hardcoded_prog = SensorCaptureProg.get(self.rom_info, self.device_info.type, 0x18,
0x19) # TODO: find where 0x18, 0x19 coming from
if self.hardcoded_prog is None: if self.hardcoded_prog is None:
raise Exception('Can\'t find initial capture program for rom %s and sensor type %x' % (repr(self.rom_info), self.device_info.type)) raise Exception('Can\'t find initial capture program for rom %s and sensor type %x' %
(repr(self.rom_info), self.device_info.type))
# Look for a "2D" chunk. It must have a 32 bit integer which represent the number of lines per frame # Look for a "2D" chunk. It must have a 32 bit integer which represent the number of lines per frame
lines_2d = [unpack('<L', v)[0] for [k, v] in prg.split_chunks(self.hardcoded_prog) if k == 0x2f][0] lines_2d = [
self.lines_per_frame = lines_2d*self.type_info.repeat_multiplier unpack('<L', v)[0] for [k, v] in prg.split_chunks(self.hardcoded_prog) if k == 0x2f
][0]
self.lines_per_frame = lines_2d * self.type_info.repeat_multiplier
self.bytes_per_line = self.type_info.bytes_per_line self.bytes_per_line = self.type_info.bytes_per_line
factory_bits = get_factory_bits(0x0e00) factory_bits = get_factory_bits(0x0e00)
@ -235,37 +265,37 @@ class Sensor():
with open(calib_data_path, 'wb') as f: with open(calib_data_path, 'wb') as f:
f.write(self.calib_data) f.write(self.calib_data)
# This is the exact logic from the DLL. # This is the exact logic from the DLL.
# If it looks broken that was probably intended. # If it looks broken that was probably intended.
def patch_timeslot_table(self, b, inc_address, mult): def patch_timeslot_table(self, b: bytes, inc_address: bool, mult: int):
b=bytearray(b) b = bytearray(b)
i=0 i = 0
while i+3 < len(b): while i + 3 < len(b):
if b[i] & 0xf8 == 0x10: if b[i] & 0xf8 == 0x10:
if b[i+2] > 1: if b[i + 2] > 1:
b[i+2] *= mult b[i + 2] *= mult
if inc_address: if inc_address:
b[i+1] += 1 b[i + 1] += 1
i+=3 i += 3
continue continue
if b[i] == 0: if b[i] == 0:
i+=1 i += 1
continue continue
if b[i] == 7: if b[i] == 7:
i+=2 i += 2
continue continue
break break
return bytes(b) return bytes(b)
def patch_timeslot_again(self, b): def patch_timeslot_again(self, b: bytes):
b=bytearray(b) b = bytearray(b)
pc = 0 pc = 0
match=None match = None
# Look for the last Call in the script # Look for the last Call in the script
while pc < len(b): while pc < len(b):
opcode, l, *operands = prg.decode_insn(b[pc:]) opcode, l, *operands = prg.decode_insn(b[pc:])
@ -276,7 +306,7 @@ class Sensor():
# Call # Call
if opcode == 11: if opcode == 11:
match = operands[1] # destination address match = operands[1] # destination address
pc += l pc += l
@ -303,88 +333,93 @@ class Sensor():
return bytes(b) return bytes(b)
# Hack the value to be taken from the factory calibration table right in the middle of a sensor # Hack the value to be taken from the factory calibration table right in the middle of a sensor
b[match+1] = self.factory_calibration_values[self.key_calibration_line] b[match + 1] = self.factory_calibration_values[self.key_calibration_line]
return bytes(b) return bytes(b)
def average(self, raw_calib_data): def average(self, raw_calib_data: bytes):
frame_size = self.lines_per_frame * self.bytes_per_line frame_size = self.lines_per_frame * self.bytes_per_line
interleave_lines = self.lines_per_frame // self.type_info.lines_per_calibration_data # 2, TODO: algo is quite different when it is 1 interleave_lines = self.lines_per_frame // self.type_info.lines_per_calibration_data # 2, TODO: algo is quite different when it is 1
input_frames = self.calibration_frames input_frames = self.calibration_frames
if interleave_lines > 1: if interleave_lines > 1:
if input_frames > 1: if input_frames > 1:
# skip the first frame # skip the first frame
input_frames -= 1 input_frames -= 1
base_address = frame_size base_address = frame_size
frame=raw_calib_data[base_address:base_address+frame_size] frame = raw_calib_data[base_address:base_address + frame_size]
# split into groups of lines # split into groups of lines
frame=chunks(frame, interleave_lines*self.bytes_per_line) frame = chunks(frame, interleave_lines * self.bytes_per_line)
# split group of lines into lines # split group of lines into lines
frame=[chunks(f, self.bytes_per_line) for f in frame] frame = [chunks(f, self.bytes_per_line) for f in frame]
# calculate averages across interleaved lines # calculate averages across interleaved lines
frame=[bytes([sum(i)//len(f) for i in zip(*f)]) for f in frame] frame = [bytes([sum(i) // len(f) for i in zip(*f)]) for f in frame]
frame=b''.join(frame) frame = b''.join(frame)
else: else:
if input_frames > 1: if input_frames > 1:
# skip the first frame # skip the first frame
input_frames -= 2 input_frames -= 2
base_address = frame_size*2 base_address = frame_size * 2
frames=raw_calib_data[base_address:base_address+frame_size*input_frames] frames = raw_calib_data[base_address:base_address + frame_size * input_frames]
frames=chunks(frames, frame_size) frames = chunks(frames, frame_size)
frame=[int(sum(i)/input_frames) for i in zip(*frames)] frame = [int(sum(i) / input_frames) for i in zip(*frames)]
frame=bytes(frame) frame = bytes(frame)
return frame return frame
def process_calibration_results(self, cooked_data): def process_calibration_results(self, cooked_data: bytes):
frame=chunks(cooked_data, self.bytes_per_line) frame = chunks(cooked_data, self.bytes_per_line)
# apply scaling factors # apply scaling factors
frame=[f[:8] + bytes(map(scale, f[8:])) for f in frame] frame = [f[:8] + bytes(map(scale, f[8:])) for f in frame]
frame=b''.join(frame) frame = b''.join(frame)
if len(self.calib_data) > 0: if len(self.calib_data) > 0:
# Not the first calibration run. Combine results # Not the first calibration run. Combine results
# split previous calibration info into lines # split previous calibration info into lines
lll=chunks(self.calib_data, self.bytes_per_line) lll = chunks(self.calib_data, self.bytes_per_line)
# split next calibration info into lines # split next calibration info into lines
rrr=chunks(frame, self.bytes_per_line) rrr = chunks(frame, self.bytes_per_line)
# Don't touch the first 8 bytes of each line, add everything else as signed characters, clipping the values # Don't touch the first 8 bytes of each line, add everything else as signed characters, clipping the values
combined=[ll[:8] + bytes([add(l, r) for l, r in zip(ll[8:],rr[8:])]) for ll, rr in zip(lll, rrr)] combined = [
ll[:8] + bytes([add(l, r) for l, r in zip(ll[8:], rr[8:])])
for ll, rr in zip(lll, rrr)
]
self.calib_data = bytes(b''.join(combined)) self.calib_data = bytes(b''.join(combined))
else: else:
self.calib_data = frame self.calib_data = frame
def get_key_line(self): def get_key_line(self):
if len(self.calib_data) > 0: if len(self.calib_data) > 0:
bytes_per_calibration_line=len(self.calib_data) // self.type_info.lines_per_calibration_data bytes_per_calibration_line = len(
key_line_offset=8+bytes_per_calibration_line*self.key_calibration_line self.calib_data) // self.type_info.lines_per_calibration_data
key_line=self.calib_data[key_line_offset:key_line_offset+self.type_info.line_width] key_line_offset = 8 + bytes_per_calibration_line * self.key_calibration_line
key_line=bytes([i-1 if i == 5 else i for i in key_line]) key_line = self.calib_data[key_line_offset:key_line_offset + self.type_info.line_width]
key_line = bytes([i - 1 if i == 5 else i for i in key_line])
else: else:
key_line=b'\0'*self.type_info.line_width key_line = b'\0' * self.type_info.line_width
return key_line return key_line
def line_update_type_1(self, mode, chunks): def line_update_type_1(self, mode: CaptureMode,
chunks: typing.List[typing.List[typing.Union[int, bytes]]]):
for c in chunks: for c in chunks:
# Timeslot Table 2D # Timeslot Table 2D
if c[0] == 0x34: if c[0] == 0x34:
# TODO: figure out when to use address increment # TODO: figure out when to use address increment
tst = self.patch_timeslot_table(c[1], True, self.type_info.repeat_multiplier) tst = self.patch_timeslot_table(c[1], True, self.type_info.repeat_multiplier)
if mode != CaptureMode.CALIBRATE: if mode != CaptureMode.CALIBRATE:
tst=self.patch_timeslot_again(tst) tst = self.patch_timeslot_again(tst)
c[1] = self.get_key_line() + tst[self.type_info.line_width:] c[1] = self.get_key_line() + tst[self.type_info.line_width:]
#---------------- Reply Configuration --------------- # ---------------- Reply Configuration ---------------
chunks += [[0x17, b'']] chunks += [[0x17, b'']]
if mode == CaptureMode.IDENTIFY: if mode == CaptureMode.IDENTIFY:
@ -392,53 +427,68 @@ class Sensor():
# It seems to be only used for identification and it looks almost identical to Finger Detect (0x26) # It seems to be only used for identification and it looks almost identical to Finger Detect (0x26)
# Seems to be the same all the time for a given sensor and mostly hardcoded # Seems to be the same all the time for a given sensor and mostly hardcoded
# TODO: analyse construct_wtf_4e @0000000180090BF0 # TODO: analyse construct_wtf_4e @0000000180090BF0
chunks += [[0x4e, unhexlify('fbb20f0000000f00300000008700020067000a00018000000a0200000b1900008813b80b01091000')]] chunks += [[
0x4e,
unhexlify(
'fbb20f0000000f00300000008700020067000a00018000000a0200000b1900008813b80b01091000'
)
]]
# Image Reconstruction. # Image Reconstruction.
# TODO: analyse add_image_reconstruction_cmd_02_buff_list_item @000000018008EA70 # TODO: analyse add_image_reconstruction_cmd_02_buff_list_item @000000018008EA70
chunks += [[0x2e, unhexlify('0200180002000000700070004d010000a0008c003c32321e3c0a0202')]] chunks += [[
0x2e, unhexlify('0200180002000000700070004d010000a0008c003c32321e3c0a0202')
]]
elif mode == CaptureMode.ENROLL: elif mode == CaptureMode.ENROLL:
chunks += [[0x26, unhexlify('fbb20f0000000f00300000008700020067000a00018000000a0200000b19000050c360ea01091000')]] chunks += [[
0x26,
unhexlify(
'fbb20f0000000f00300000008700020067000a00018000000a0200000b19000050c360ea01091000'
)
]]
# Image Reconstruction. There is only one byte difference with the "identify" version. (same is true for 0097) # Image Reconstruction. There is only one byte difference with the "identify" version. (same is true for 0097)
chunks += [[0x2e, unhexlify('0200180023000000700070004d010000a0008c003c32321e3c0a0202')]] chunks += [[
0x2e, unhexlify('0200180023000000700070004d010000a0008c003c32321e3c0a0202')
]]
#---------------- Interleave --------------- # ---------------- Interleave ---------------
chunks += [[0x44, pack('<L', 1)]] chunks += [[0x44, pack('<L', 1)]]
lines=[] lines: typing.List[Line] = []
cnt=2 # TODO figure out why 2 cnt = 2 # TODO figure out why 2
l=Line() l = Line()
lines += [l] lines += [l]
l.mask = 0xff l.mask = 0xff
# Find 2nd "Enable Rx" instruction # Find 2nd "Enable Rx" instruction
pc, _ = prg.find_nth_insn(tst, 6, 2) pc, _ = prg.find_nth_insn(tst, 6, 2)
l.flags = (pc + 1) | (cnt << 0x14) | 0x7000000 l.flags = (pc + 1) | (cnt << 0x14) | 0x7000000
l.data = self.type_info.calibration_blob l.data = self.type_info.calibration_blob
l.v0 = 0xf l.v0 = 0xf
cnt += 1 cnt += 1
l=Line() l = Line()
lines += [l] lines += [l]
l.mask = 0xff l.mask = 0xff
# Find 1st "Write Register" instruction to the 0x8000203C port # Find 1st "Write Register" instruction to the 0x8000203C port
pc, _ = prg.find_nth_regwrite(tst, 0x8000203C, 1) pc, _ = prg.find_nth_regwrite(tst, 0x8000203C, 1)
l.flags = (pc + 1) | (cnt << 0x14) | 0x7000000 l.flags = (pc + 1) | (cnt << 0x14) | 0x7000000
l.v0, l.v1, l.data = bitpack(self.factory_calibration_values) l.v0, l.v1, l.data = bitpack(self.factory_calibration_values)
l.v0 = (l.v0-1) | 8 l.v0 = (l.v0 - 1) | 8
cnt += 1 cnt += 1
if len(self.calib_data) > 0: if len(self.calib_data) > 0:
bytes_per_calibration_line=len(self.calib_data) // self.type_info.lines_per_calibration_data bytes_per_calibration_line = len(
self.calib_data) // self.type_info.lines_per_calibration_data
for i in range(0, 112, 4): for i in range(0, 112, 4):
l=Line() l = Line()
lines += [l] lines += [l]
l.mask=0xffffffff l.mask = 0xffffffff
l.flags=i | (0x85 << 24) l.flags = i | (0x85 << 24)
l.data=b'' l.data = b''
for j in range(0, 112): for j in range(0, 112):
p=8+j*bytes_per_calibration_line+i p = 8 + j * bytes_per_calibration_line + i
l.data += self.calib_data[p:p+4] l.data += self.calib_data[p:p + 4]
# Align to dwords, as the sensor demands it # Align to dwords, as the sensor demands it
for l in lines: for l in lines:
@ -446,25 +496,28 @@ class Sensor():
if pad > 0: if pad > 0:
l.data += b'\0' * (4 - pad) l.data += b'\0' * (4 - pad)
# ---------------- Line Update ---------------
#---------------- Line Update ---------------
line_update = pack('<L', len(lines)) line_update = pack('<L', len(lines))
line_update += b''.join([pack('<LL', l.mask, l.flags) for l in lines]) line_update += b''.join([pack('<LL', l.mask, l.flags) for l in lines])
line_update += b''.join([l.data for l in lines if ((l.flags & 0x00f00000) >> 0x14) <= 1]) line_update += b''.join([l.data for l in lines if ((l.flags & 0x00f00000) >> 0x14) <= 1])
chunks += [[0x30, line_update]] chunks += [[0x30, line_update]]
#---------------- Line Update Transform --------------- # ---------------- Line Update Transform ---------------
update_transform = b''.join([pack('<BBH', l.v0, l.v1, l.v2) + l.data for l in lines if ((l.flags & 0x00f00000) >> 0x14) > 1]) update_transform = b''.join([
pack('<BBH', l.v0, l.v1, l.v2) + l.data for l in lines
if ((l.flags & 0x00f00000) >> 0x14) > 1
])
chunks += [[0x43, update_transform]] chunks += [[0x43, update_transform]]
return chunks return chunks
def line_update_type_2(self, mode, chunks): def line_update_type_2(self, mode: CaptureMode,
chunks: typing.List[typing.List[typing.Union[int, bytes]]]):
for c in chunks: for c in chunks:
# patch the 2D params. # patch the 2D params.
# The following is only needed on some rom versions below 6.5 as reported by cmd_01 # The following is only needed on some rom versions below 6.5 as reported by cmd_01
#if c[0] == 0x2f: # if c[0] == 0x2f:
# c[1] = pack('<L', unpack('<L', c[1])[0]*mult) # c[1] = pack('<L', unpack('<L', c[1])[0]*mult)
# Timeslot Table 2D # Timeslot Table 2D
@ -472,10 +525,10 @@ class Sensor():
# TODO: figure out when to use address increment # TODO: figure out when to use address increment
tst = self.patch_timeslot_table(c[1], True, self.type_info.repeat_multiplier) tst = self.patch_timeslot_table(c[1], True, self.type_info.repeat_multiplier)
if mode != CaptureMode.CALIBRATE: if mode != CaptureMode.CALIBRATE:
tst=self.patch_timeslot_again(tst) tst = self.patch_timeslot_again(tst)
c[1] = tst c[1] = tst
#---------------- Reply Configuration --------------- # ---------------- Reply Configuration ---------------
chunks += [[0x17, b'']] chunks += [[0x17, b'']]
if mode == CaptureMode.IDENTIFY: if mode == CaptureMode.IDENTIFY:
@ -483,38 +536,52 @@ class Sensor():
# It seems to be only used for identification and it looks almost identical to Finger Detect (0x26) # It seems to be only used for identification and it looks almost identical to Finger Detect (0x26)
# Seems to be the same all the time for a given sensor and mostly hardcoded # Seems to be the same all the time for a given sensor and mostly hardcoded
# TODO: analyse construct_wtf_4e @0000000180090BF0 # TODO: analyse construct_wtf_4e @0000000180090BF0
chunks += [[0x4e, unhexlify('fbb20f0000000f00300000006001020040010a00018000000a0200000b1900008813b80b01091000')]] chunks += [[
0x4e,
unhexlify(
'fbb20f0000000f00300000006001020040010a00018000000a0200000b1900008813b80b01091000'
)
]]
# Image Reconstruction. # Image Reconstruction.
# TODO: analyse add_image_reconstruction_cmd_02_buff_list_item @000000018008EA70 # TODO: analyse add_image_reconstruction_cmd_02_buff_list_item @000000018008EA70
chunks += [[0x2e, unhexlify('0200180002000000900090004d01000090017c013c323232640a0201')]] chunks += [[
0x2e, unhexlify('0200180002000000900090004d01000090017c013c323232640a0201')
]]
elif mode == CaptureMode.ENROLL: elif mode == CaptureMode.ENROLL:
chunks += [[0x26, unhexlify('fbb20f0000000f00300000006001020040010a00018000000a0200000b19000050c360ea01091000')]] chunks += [[
0x26,
unhexlify(
'fbb20f0000000f00300000006001020040010a00018000000a0200000b19000050c360ea01091000'
)
]]
# Image Reconstruction. There is only one byte difference with the "identify" version. (same is true for 0097) # Image Reconstruction. There is only one byte difference with the "identify" version. (same is true for 0097)
chunks += [[0x2e, unhexlify('0200180023000000900090004d01000090017c013c323232640a0201')]] chunks += [[
0x2e, unhexlify('0200180023000000900090004d01000090017c013c323232640a0201')
]]
lines = [] lines = []
l=Line() l = Line()
lines += [l] lines += [l]
l.mask = 0xff l.mask = 0xff
# Find 2nd "Enable Rx" instruction # Find 2nd "Enable Rx" instruction
pc, _ = prg.find_nth_insn(tst, 6, 2) pc, _ = prg.find_nth_insn(tst, 6, 2)
l.flags = (pc + 1) | 0x3000000 l.flags = (pc + 1) | 0x3000000
l.data = self.type_info.calibration_blob l.data = self.type_info.calibration_blob
l=Line() l = Line()
lines += [l] lines += [l]
l.mask = 0xff l.mask = 0xff
# Find 1st "Write Register" instruction to the 0x8000203C port # Find 1st "Write Register" instruction to the 0x8000203C port
pc, _ = prg.find_nth_regwrite(tst, 0x800020fc, 1) pc, _ = prg.find_nth_regwrite(tst, 0x800020fc, 1)
l.flags = (pc + 1) | 0x3000000 l.flags = (pc + 1) | 0x3000000
l.data = self.factory_calib_data l.data = self.factory_calib_data
l=Line() l = Line()
lines += [l] lines += [l]
l.mask = 0xff l.mask = 0xff
# Find 1st "Write Register" instruction to the 0x8000203C port # Find 1st "Write Register" instruction to the 0x8000203C port
pc, _ = prg.find_nth_regwrite(tst, 0x8000203c, 1) pc, _ = prg.find_nth_regwrite(tst, 0x8000203c, 1)
l.flags = (pc + 1) | 0x3000000 l.flags = (pc + 1) | 0x3000000
l.data = self.factory_calibration_values l.data = self.factory_calibration_values
@ -524,8 +591,7 @@ class Sensor():
if pad > 0: if pad > 0:
l.data += b'\0' * (4 - pad) l.data += b'\0' * (4 - pad)
# ---------------- Line Update ---------------
#---------------- Line Update ---------------
line_update = pack('<L', len(lines)) line_update = pack('<L', len(lines))
line_update += b''.join([pack('<LL', l.mask, l.flags) for l in lines]) line_update += b''.join([pack('<LL', l.mask, l.flags) for l in lines])
@ -534,25 +600,25 @@ class Sensor():
return chunks return chunks
def build_cmd_02(self, mode): def build_cmd_02(self, mode: CaptureMode):
chunks=list(prg.split_chunks(self.hardcoded_prog)) chunks = list(prg.split_chunks(self.hardcoded_prog))
if self.rom_info.product != 0x30: if self.rom_info.product != 0x30:
raise Exception('Not implemented') raise Exception('Not implemented')
if self.device_info.type in line_update_type1_devices: if self.device_info.type in line_update_type1_devices:
chunks=self.line_update_type_1(mode, chunks) chunks = self.line_update_type_1(mode, chunks)
else: else:
chunks=self.line_update_type_2(mode, chunks) chunks = self.line_update_type_2(mode, chunks)
if mode == CaptureMode.CALIBRATE: if mode == CaptureMode.CALIBRATE:
req_lines = self.calibration_frames*self.lines_per_frame+1 # TODO: figure out how this is actually calculated req_lines = self.calibration_frames * self.lines_per_frame + 1 # TODO: figure out how this is actually calculated
else: else:
req_lines = 0 req_lines = 0
return pack('<BHH', 2, self.bytes_per_line, req_lines) + prg.merge_chunks(chunks) return pack('<BHH', 2, self.bytes_per_line, req_lines) + prg.merge_chunks(chunks)
def persist_clean_slate(self, clean_slate): def persist_clean_slate(self, clean_slate: bytes):
start = read_flash(6, 0, 0x44) start = read_flash(6, 0, 0x44)
if start != b'\xff' * 0x44: if start != b'\xff' * 0x44:
@ -574,8 +640,8 @@ class Sensor():
return False return False
hs, zeroes = start[0:0x20], start[0x20:0x40] hs, zeroes = start[0:0x20], start[0x20:0x40]
if zeroes != b'\0'*0x20: if zeroes != b'\0' * 0x20:
logging.warning('Unexpected contents in calibration flash partition') logging.warning('Unexpected contents in calibration flash partition')
return False return False
@ -586,7 +652,6 @@ class Sensor():
return True return True
def calibrate(self): def calibrate(self):
if os.path.isfile(calib_data_path): if os.path.isfile(calib_data_path):
with open(calib_data_path, 'rb') as f: with open(calib_data_path, 'rb') as f:
@ -615,8 +680,9 @@ class Sensor():
clean_slate = self.average(usb.read_82()) clean_slate = self.average(usb.read_82())
clean_slate = pack('<H', len(clean_slate)) + clean_slate clean_slate = pack('<H', len(clean_slate)) + clean_slate
clean_slate = clean_slate + pack('<H', 0) # TODO: still don't know what this zero is for clean_slate = clean_slate + pack('<H', 0) # TODO: still don't know what this zero is for
clean_slate = pack('<H', len(clean_slate)) + sha256(clean_slate).digest() + b'\0'*0x20 + clean_slate clean_slate = pack(
'<H', len(clean_slate)) + sha256(clean_slate).digest() + b'\0' * 0x20 + clean_slate
clean_slate = pack('<H', 0x5002) + clean_slate clean_slate = pack('<H', 0x5002) + clean_slate
self.persist_clean_slate(clean_slate) self.persist_clean_slate(clean_slate)
@ -625,7 +691,7 @@ class Sensor():
def cancel(self): def cancel(self):
usb.cancel = True usb.cancel = True
def capture(self, mode): def capture(self, mode: CaptureMode) -> typing.Tuple[int, int, int, int]:
try: try:
assert_status(tls.app(self.build_cmd_02(mode))) assert_status(tls.app(self.build_cmd_02(mode)))
@ -638,8 +704,8 @@ class Sensor():
while True: while True:
b = usb.wait_int() b = usb.wait_int()
if b[0] == 2: if b[0] == 2:
break; break
# wait capture complete # wait capture complete
while True: while True:
b = usb.wait_int() b = usb.wait_int()
@ -648,12 +714,12 @@ class Sensor():
if b[2] & 4: if b[2] & 4:
break break
res = get_prg_status2() res = get_prg_status2()
assert_status(res) assert_status(res)
res = res[2:] res = res[2:]
l, res = res[:4], res[4:] l, res = res[:4], res[4:]
l, = unpack('<L', l) l, = unpack('<L', l)
@ -665,14 +731,13 @@ class Sensor():
if error != 0: if error != 0:
raise Exception('Scanning problem: %04x' % error) raise Exception('Scanning problem: %04x' % error)
return (x, y, w1, w2) return x, y, w1, w2
finally: finally:
tls.app(unhexlify('04')) # capture stop if still running, cleanup tls.app(unhexlify('04')) # capture stop if still running, cleanup
def enrollment_update_start(self, key: int) -> int:
def enrollment_update_start(self, key): rsp = tls.app(pack('<BLL', 0x68, key, 0))
rsp=tls.app(pack('<BLL', 0x68, key, 0))
assert_status(rsp) assert_status(rsp)
new_key, = unpack('<L', rsp[2:]) new_key, = unpack('<L', rsp[2:])
@ -687,19 +752,19 @@ class Sensor():
assert_status(tls.app(pack('<BL', 0x69, 0))) assert_status(tls.app(pack('<BL', 0x69, 0)))
# Generates interrupt # Generates interrupt
def enrollment_update(self, prev): def enrollment_update(self, prev: bytes):
write_enable() write_enable()
try: try:
rsp=tls.app(b'\x6b' + prev) rsp = tls.app(b'\x6b' + prev)
assert_status(rsp) assert_status(rsp)
finally: finally:
call_cleanups() call_cleanups()
return rsp[2:] return rsp[2:]
def append_new_image(self, prev): def append_new_image(self, prev: bytes):
self.enrollment_update(prev) self.enrollment_update(prev)
usb.wait_int() usb.wait_int()
res = self.enrollment_update(prev) res = self.enrollment_update(prev)
@ -709,27 +774,26 @@ class Sensor():
if l != len(res): if l != len(res):
raise Exception('Response size does not match %d != %d', l, len(res)) raise Exception('Response size does not match %d != %d', l, len(res))
magic_len = 0x38 # hardcoded in the DLL magic_len = 0x38 # hardcoded in the DLL
template = header = tid = None template = header = tid = None
while len(res) > 0: while len(res) > 0:
tag, l = unpack('<HH', res[:4]) tag, l = unpack('<HH', res[:4])
if tag == 0: if tag == 0:
template = res[:magic_len+l] template = res[:magic_len + l]
elif tag == 1: elif tag == 1:
header = res[magic_len:magic_len+l] header = res[magic_len:magic_len + l]
elif tag == 3: elif tag == 3:
tid = res[magic_len:magic_len+l] tid = res[magic_len:magic_len + l]
else: else:
logging.warning('Ignoring unknown tag %x' % tag) logging.warning('Ignoring unknown tag %x' % tag)
res=res[magic_len+l:]
return (header, template, tid) res = res[magic_len + l:]
def make_finger_data(self, subtype, template, tid): return header, template, tid
def make_finger_data(self, subtype: int, template: bytes, tid: bytes):
template = pack('<HH', 1, len(template)) + template template = pack('<HH', 1, len(template)) + template
tid = pack('<HH', 2, len(tid)) + tid tid = pack('<HH', 2, len(tid)) + tid
@ -743,15 +807,15 @@ class Sensor():
# TODO: Better typing information needed. # TODO: Better typing information needed.
def enroll(self, identity: SidIdentity, subtype: int, def enroll(self, identity: SidIdentity, subtype: int,
update_cb: typing.Callable[[typing.Any, typing.Optional[Exception]], None]): update_cb: typing.Callable[[typing.Any, typing.Optional[Exception]], None]):
def do_create_finger(final_template, tid): def do_create_finger(final_template: bytes, tid: bytes):
tinfo = self.make_finger_data(subtype, final_template, tid) tinfo = self.make_finger_data(subtype, final_template, tid)
usr=db.lookup_user(identity) usr = db.lookup_user(identity)
if usr == None: if usr is None:
usr = db.new_user(identity) usr = db.new_user(identity)
else: else:
usr = usr.dbid usr = usr.dbid
recid = db.new_finger(usr, tinfo) recid = db.new_finger(usr, tinfo)
usb.wait_int() usb.wait_int()
@ -759,8 +823,8 @@ class Sensor():
return recid return recid
key=0 key = 0
template=b'' template = b''
self.create_enrollment() self.create_enrollment()
while True: while True:
try: try:
@ -785,11 +849,11 @@ class Sensor():
finally: finally:
self.enrollment_update_end() self.enrollment_update_end()
self.enrollment_update_end() # done twice for some reason self.enrollment_update_end() # done twice for some reason
return do_create_finger(template, tid) return do_create_finger(template, tid)
def parse_dict(self, x): def parse_dict(self, x: bytes):
rc={} rc = {}
while len(x) > 0: while len(x) > 0:
(t, l), x = unpack('<HH', x[:4]), x[4:] (t, l), x = unpack('<HH', x[:4]), x[4:]
@ -799,10 +863,10 @@ class Sensor():
def match_finger(self) -> typing.Tuple[int, int, bytes]: def match_finger(self) -> typing.Tuple[int, int, bytes]:
try: try:
stg_id=0 # match against any storage stg_id = 0 # match against any storage
usr_id=0 # match against any user usr_id = 0 # match against any user
cmd=pack('<BBBHHHHH', 0x5e, 2, 0xff, stg_id, usr_id, 1, 0,0) cmd = pack('<BBBHHHHH', 0x5e, 2, 0xff, stg_id, usr_id, 1, 0, 0)
rsp=tls.app(cmd) rsp = tls.app(cmd)
assert_status(rsp) assert_status(rsp)
b = usb.wait_int() b = usb.wait_int()
@ -814,23 +878,22 @@ class Sensor():
assert_status(rsp) assert_status(rsp)
rsp = rsp[2:] rsp = rsp[2:]
(l,), rsp = unpack('<H', rsp[:2]), rsp[2:] (l, ), rsp = unpack('<H', rsp[:2]), rsp[2:]
if l != len(rsp): if l != len(rsp):
raise Exception('Response size does not match') raise Exception('Response size does not match')
rsp=self.parse_dict(rsp) rsp = self.parse_dict(rsp)
usrid, subtype, hsh = rsp[1], rsp[3], rsp[4] usrid, subtype, hsh = rsp[1], rsp[3], rsp[4]
usrid, = unpack('<L', usrid) usrid, = unpack('<L', usrid)
subtype, = unpack('<H', subtype) subtype, = unpack('<H', subtype)
return (usrid, subtype, hsh) return usrid, subtype, hsh
finally: finally:
# cleanup, ignore any errors # cleanup, ignore any errors
tls.app(unhexlify('6200000000')) tls.app(unhexlify('6200000000'))
def identify(self, update_cb: typing.Callable[[Exception], None]):
def identify(self, update_cb):
while True: while True:
try: try:
glow_start_scan() glow_start_scan()
@ -848,17 +911,17 @@ class Sensor():
return self.match_finger() return self.match_finger()
def get_finger_blobs(self, usrid, subtype): def get_finger_blobs(self, usrid: int, subtype: int):
usr = db.get_user(usrid) usr = db.get_user(usrid)
fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype] fingerids = [f['dbid'] for f in usr.fingers if f['subtype'] == subtype]
if len(fingerids) != 1: if len(fingerids) != 1:
raise Exception('Unexpected matching finger count') raise Exception('Unexpected matching finger count')
finger_record = db.get_record_children(fingerids[0]) finger_record = db.get_record_children(fingerids[0])
ids=[r['dbid'] for r in finger_record.children if r['type'] == 8] ids = [r['dbid'] for r in finger_record.children if r['type'] == 8]
return [db.get_record_value(id).value for id in ids] return [db.get_record_value(id).value for id in ids]
sensor = Sensor()
sensor = Sensor()

View file

@ -1,15 +1,15 @@
from struct import unpack, pack
import typing import typing
from struct import unpack, pack
class SidIdentity():
class SidIdentity:
def __init__(self, revision: int, auth: int, subauth: typing.Sequence[int]): def __init__(self, revision: int, auth: int, subauth: typing.Sequence[int]):
self.revision = revision self.revision = revision
self.auth = auth self.auth = auth
self.subauth = subauth self.subauth = subauth
def to_bytes(self): def to_bytes(self):
b=pack('>BBHL', self.revision, len(self.subauth), self.auth >> 32, self.auth & 0xffffffff) b = pack('>BBHL', self.revision, len(self.subauth), self.auth >> 32, self.auth & 0xffffffff)
for i in self.subauth: for i in self.subauth:
b += pack('<L', i) b += pack('<L', i)
@ -18,6 +18,7 @@ class SidIdentity():
def __repr__(self): def __repr__(self):
return 'S-%d-%d-%s' % (self.revision, self.auth, '-'.join(map(str, self.subauth))) return 'S-%d-%d-%s' % (self.revision, self.auth, '-'.join(map(str, self.subauth)))
def sid_from_bytes(b: bytes): def sid_from_bytes(b: bytes):
revision = b[0] revision = b[0]
subcnt = b[1] subcnt = b[1]
@ -26,11 +27,12 @@ def sid_from_bytes(b: bytes):
for i in b[2:8]: for i in b[2:8]:
auth <<= 8 auth <<= 8
auth |= i auth |= i
subauth=unpack('<%dL' % subcnt, b[8:]) subauth = unpack('<%dL' % subcnt, b[8:])
return SidIdentity(revision, auth, subauth) return SidIdentity(revision, auth, subauth)
def sid_from_string(s: str): def sid_from_string(s: str):
parts = s.split('-') parts = s.split('-')

View file

@ -1,28 +1,33 @@
import typing
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
class SensorTypeInfo: class SensorTypeInfo:
table=[] table: typing.List["SensorTypeInfo"] = []
@classmethod @classmethod
def get_by_type(cls, sensor_type): def get_by_type(cls, sensor_type: int) -> typing.Optional["SensorTypeInfo"]:
# noinspection PyUnresolvedReferences
from . import generated_tables from . import generated_tables
for i in cls.table: for i in cls.table:
if i.sensor_type == sensor_type: if i.sensor_type == sensor_type:
return i return i
def __init__(self, sensor_type, bytes_per_line, repeat_multiplier, lines_per_calibration_data, line_width, calibration_blob): def __init__(self, sensor_type: int, bytes_per_line: int, repeat_multiplier: int,
self.sensor_type=sensor_type lines_per_calibration_data: int, line_width: int, calibration_blob: str):
self.repeat_multiplier=repeat_multiplier self.sensor_type = sensor_type
self.lines_per_calibration_data=lines_per_calibration_data self.repeat_multiplier = repeat_multiplier
self.line_width=line_width self.lines_per_calibration_data = lines_per_calibration_data
self.bytes_per_line=bytes_per_line self.line_width = line_width
self.calibration_blob=unhexlify(calibration_blob) self.bytes_per_line = bytes_per_line
self.calibration_blob = unhexlify(calibration_blob)
def __repr__(self): def __repr__(self):
calibration_blob=hexlify(self.calibration_blob).decode() calibration_blob = hexlify(self.calibration_blob).decode()
return 'SensorTypeInfo(sensor_type=0x%04x, bytes_per_line=0x%x, repeat_multiplier=%d, lines_per_calibration_data=%d, line_width=%d, calibration_blob=%s)' % ( return 'SensorTypeInfo(sensor_type=0x%04x, bytes_per_line=0x%x, repeat_multiplier=%d, lines_per_calibration_data=%d, line_width=%d, calibration_blob=%s)' % (
self.sensor_type, self.bytes_per_line, self.repeat_multiplier, self.lines_per_calibration_data, self.line_width, repr(calibration_blob)) self.sensor_type, self.bytes_per_line, self.repeat_multiplier,
self.lines_per_calibration_data, self.line_width, repr(calibration_blob))
def fuzzy(expected, actual): def fuzzy(expected, actual):
if expected == actual: if expected == actual:
@ -32,6 +37,7 @@ def fuzzy(expected, actual):
else: else:
return 0 return 0
def metric(i, rominfo): def metric(i, rominfo):
metric = 0 metric = 0
metric |= fuzzy(i.major, rominfo.major) metric |= fuzzy(i.major, rominfo.major)
@ -42,13 +48,15 @@ def metric(i, rominfo):
metric <<= 2 metric <<= 2
metric |= fuzzy(i.u1, rominfo.u1) metric |= fuzzy(i.u1, rominfo.u1)
return metric return metric
class SensorCaptureProg: class SensorCaptureProg:
table=[] table: typing.List["SensorCaptureProg"] = []
@classmethod @classmethod
def get(cls, rominfo, sensor_type, a0, a1): def get(cls, rominfo, sensor_type: int, a0: int, a1: int):
# noinspection PyUnresolvedReferences
from . import generated_tables from . import generated_tables
maximum = 0 maximum = 0
@ -74,20 +82,20 @@ class SensorCaptureProg:
if found is not None: if found is not None:
return b''.join(found.blobs) return b''.join(found.blobs)
def __init__(self, major, minor, build, u1, dev_type, a0, a1, blobs): def __init__(self, major: int, minor: int, build: int, u1: int, dev_type: int, a0: int, a1: int,
self.major=major blobs: typing.Sequence[str]):
self.minor=minor self.major = major
self.build=build self.minor = minor
self.u1=u1 self.build = build
self.dev_type=dev_type self.u1 = u1
self.a0=a0 self.dev_type = dev_type
self.a1=a1 self.a0 = a0
blobs=[unhexlify(b) for b in blobs] self.a1 = a1
self.blobs=blobs self.blobs = [unhexlify(b) for b in blobs]
def __repr__(self): def __repr__(self):
blobs=[hexlify(b).decode() for b in self.blobs] blobs = [hexlify(b).decode() for b in self.blobs]
return 'SensorCaptureProg(major=0x%x, minor=0x%x, build=0x%x, u1=0x%x, dev_type=0x%x, a0=0x%x, a1=0x%x, blobs=%s)' % ( return 'SensorCaptureProg(major=0x%x, minor=0x%x, build=0x%x, u1=0x%x, dev_type=0x%x, a0=0x%x, a1=0x%x, blobs=%s)' % (
self.major, self.minor, self.build, self.u1, self.dev_type, self.a0, self.a1, self.major, self.minor, self.build, self.u1, self.dev_type, self.a0, self.a1,

View file

@ -1,151 +1,155 @@
import typing
from binascii import hexlify from binascii import hexlify
from struct import unpack, pack from struct import unpack, pack
codes={} codes = {}
codes[0x0] = "No Operation" codes[0x0] = "No Operation"
codes[0x1] = "Swipe" codes[0x1] = "Swipe"
codes[0x2] = "Timeslot Configuration" codes[0x2] = "Timeslot Configuration"
codes[0x3] = "Register" codes[0x3] = "Register"
codes[0x4] = "Register Set 32" codes[0x4] = "Register Set 32"
codes[0x5] = "Register Operation 32" codes[0x5] = "Register Operation 32"
codes[0x6] = "Security" codes[0x6] = "Security"
codes[0x7] = "WOE" codes[0x7] = "WOE"
codes[0x8] = "Motion 1" codes[0x8] = "Motion 1"
codes[0xa] = "CPUCLK" codes[0xa] = "CPUCLK"
codes[0xb] = "Motion 2" codes[0xb] = "Motion 2"
codes[0xc] = "Calibration Block" codes[0xc] = "Calibration Block"
codes[0xd] = "Sweep" codes[0xd] = "Sweep"
codes[0xe] = "Zone Configuration" codes[0xe] = "Zone Configuration"
codes[0xf] = "Zones Per Sweep" codes[0xf] = "Zones Per Sweep"
codes[0x10] = "Lines Per Sweep Iteration" codes[0x10] = "Lines Per Sweep Iteration"
codes[0x11] = "Lines Per Sweep" codes[0x11] = "Lines Per Sweep"
codes[0x12] = "Total Zones" codes[0x12] = "Total Zones"
codes[0x13] = "CAL WOE Ctrl" codes[0x13] = "CAL WOE Ctrl"
codes[0x14] = "Cal WOE Mask" codes[0x14] = "Cal WOE Mask"
codes[0x15] = "BW Reduciton" codes[0x15] = "BW Reduciton"
codes[0x16] = "AGC" codes[0x16] = "AGC"
codes[0x17] = "Reply Configuration" codes[0x17] = "Reply Configuration"
codes[0x18] = "Motion 3" codes[0x18] = "Motion 3"
codes[0x19] = "WOVAR" codes[0x19] = "WOVAR"
codes[0x1a] = "Block MOde" codes[0x1a] = "Block MOde"
codes[0x1b] = "Bit Reduction" codes[0x1b] = "Bit Reduction"
codes[0x1c] = "Motion 4" codes[0x1c] = "Motion 4"
codes[0x1d] = "Calibration WOENF" codes[0x1d] = "Calibration WOENF"
codes[0x1e] = "Calibration" codes[0x1e] = "Calibration"
codes[0x1f] = "Zone Configuration A" codes[0x1f] = "Zone Configuration A"
codes[0x20] = "Set Register 32" codes[0x20] = "Set Register 32"
codes[0x21] = "Register Operation 32A" codes[0x21] = "Register Operation 32A"
codes[0x22] = "Fingerprint Buffering" codes[0x22] = "Fingerprint Buffering"
codes[0x23] = "Reply Config + Timeslot Table" codes[0x23] = "Reply Config + Timeslot Table"
codes[0x24] = "Baseline" codes[0x24] = "Baseline"
codes[0x25] = "SO Alternate" codes[0x25] = "SO Alternate"
codes[0x26] = "Finger Detect" codes[0x26] = "Finger Detect"
codes[0x27] = "Finger Detect Sample Register" codes[0x27] = "Finger Detect Sample Register"
codes[0x28] = "Finger Detect Scan Registers" codes[0x28] = "Finger Detect Scan Registers"
codes[0x29] = "Timeslot Table Offset" codes[0x29] = "Timeslot Table Offset"
codes[0x2a] = "ACM Config" codes[0x2a] = "ACM Config"
codes[0x2b] = "ACM Control" codes[0x2b] = "ACM Control"
codes[0x2c] = "CEM Config" codes[0x2c] = "CEM Config"
codes[0x2d] = "CEM Control" codes[0x2d] = "CEM Control"
codes[0x2e] = "Image Reconstruction" codes[0x2e] = "Image Reconstruction"
codes[0x2f] = "2D" codes[0x2f] = "2D"
codes[0x30] = "Line Update" codes[0x30] = "Line Update"
codes[0x31] = "FDetect Timeslot Table" codes[0x31] = "FDetect Timeslot Table"
codes[0x32] = "Register List 16" codes[0x32] = "Register List 16"
codes[0x33] = "Register list 32" codes[0x33] = "Register list 32"
codes[0x34] = "Timeslot Table 2D" codes[0x34] = "Timeslot Table 2D"
codes[0x35] = "Timeslot Table Offset for Finger Detect" codes[0x35] = "Timeslot Table Offset for Finger Detect"
codes[0x36] = "Security Aligned" codes[0x36] = "Security Aligned"
codes[0x37] = "WOF2" codes[0x37] = "WOF2"
codes[0x38] = "WOE WOF" codes[0x38] = "WOE WOF"
codes[0x39] = "Navigation" codes[0x39] = "Navigation"
codes[0x3a] = "WOE WOF2 Version2" codes[0x3a] = "WOE WOF2 Version2"
codes[0x3b] = "Cal WOE WOF2" codes[0x3b] = "Cal WOE WOF2"
codes[0x3c] = "Event Signal" codes[0x3c] = "Event Signal"
codes[0x3d] = "IFS Frame Stats" codes[0x3d] = "IFS Frame Stats"
codes[0x3e] = "SNR Method 4" codes[0x3e] = "SNR Method 4"
codes[0x3f] = "WOE WOF2 Version 3" codes[0x3f] = "WOE WOF2 Version 3"
codes[0x40] = "Calibrate WOE WOF2 Version 3" codes[0x40] = "Calibrate WOE WOF2 Version 3"
codes[0x41] = "Finger Detect Ratchet" codes[0x41] = "Finger Detect Ratchet"
codes[0x42] = "Data Encoder" codes[0x42] = "Data Encoder"
codes[0x43] = "Line Update Transform" codes[0x43] = "Line Update Transform"
codes[0x44] = "Line Update InterLeave" codes[0x44] = "Line Update InterLeave"
codes[0x45] = "SO Table Values for Macros" codes[0x45] = "SO Table Values for Macros"
codes[0x46] = "Timeslot Macro Definitions" codes[0x46] = "Timeslot Macro Definitions"
codes[0x47] = "Enable ASP Feature" codes[0x47] = "Enable ASP Feature"
codes[0x48] = "Baseline Frame" codes[0x48] = "Baseline Frame"
codes[0x49] = "Rx Select" codes[0x49] = "Rx Select"
codes[0x4e] = "WTF" codes[0x4e] = "WTF"
codes[0xffff] = "Unknown" codes[0xffff] = "Unknown"
insn_to_string=[ insn_to_string = [
'NOOP', # 0 'NOOP', # 0
'End of Table', # 1 'End of Table', # 1
'Return', # 2 'Return', # 2
'Clear SO', # 3 'Clear SO', # 3
'End of Data', # 4 'End of Data', # 4
'Marco %02x', # 5 'Marco %02x', # 5
'Enable Rx 0x%02x', # 6 'Enable Rx 0x%02x', # 6
'Idle Rx 0x%03x', # 7 'Idle Rx 0x%03x', # 7
'Enable SO 0x%03x', # 8 'Enable SO 0x%03x', # 8
'Disable SO 0x%03x', # 9 'Disable SO 0x%03x', # 9
'Interrupt %x', # 10 'Interrupt %x', # 10
'Call rx_inc=%d, addr=%x, repeat=%d', # 11 'Call rx_inc=%d, addr=%x, repeat=%d', # 11
'Features %02x', # 12 'Features %02x', # 12
'Register Write *0x%08x = 0x%04x', # 13 'Register Write *0x%08x = 0x%04x', # 13
'Sample %x, %x', # 14 'Sample %x, %x', # 14
'Sample Repeat %x, %x, repeat=%x', # 15 'Sample Repeat %x, %x, repeat=%x', # 15
] ]
def decode_insn(b):
def decode_insn(b: bytes):
if b[0] == 0: if b[0] == 0:
return (0, 1) return 0, 1
elif b[0] == 1: elif b[0] == 1:
return (1, 1) return 1, 1
elif b[0] == 2: elif b[0] == 2:
return (2, 1) return 2, 1
elif b[0] == 3: elif b[0] == 3:
return (3, 1) return 3, 1
elif b[0] == 4: elif b[0] == 4:
return (4, 1) return 4, 1
elif b[0] == 5: elif b[0] == 5:
return (5, 2, b[1]) return 5, 2, b[1]
elif b[0] == 6: elif b[0] == 6:
return (6, 2, b[1]) return 6, 2, b[1]
elif b[0] == 7: elif b[0] == 7:
return (7, 2, 0x100 if b[1] == 0 else b[1]) return 7, 2, 0x100 if b[1] == 0 else b[1]
elif b[0] & 0xfe == 8: elif b[0] & 0xfe == 8:
return (8, 2, (b[0] & 1) << 8 | b[1]) return 8, 2, (b[0] & 1) << 8 | b[1]
elif b[0] & 0xfe == 0xa: elif b[0] & 0xfe == 0xa:
return (9, 2, (b[0] & 1) << 8 | b[1]) return 9, 2, (b[0] & 1) << 8 | b[1]
elif b[0] & 0xfc == 0xc: elif b[0] & 0xfc == 0xc:
return (10, 1, b[0] & 3) return 10, 1, b[0] & 3
elif b[0] & 0xf8 == 0x10: elif b[0] & 0xf8 == 0x10:
return (11, 3, b[0] & 7, b[1] << 2, 0x100 if b[2] == 0 else b[2]) return 11, 3, b[0] & 7, b[1] << 2, 0x100 if b[2] == 0 else b[2]
elif b[0] & 0xe0 == 0x20: elif b[0] & 0xe0 == 0x20:
return (12, 1, b[0] & 0x1f) # TODO check how features are converted to op args return 12, 1, b[0] & 0x1f # TODO check how features are converted to op args
elif b[0] & 0xc0 == 0x40: elif b[0] & 0xc0 == 0x40:
return (13, 3, (b[0] & 0x3f)*4+0x80002000, b[1] | (b[2] << 8)) return 13, 3, (b[0] & 0x3f) * 4 + 0x80002000, b[1] | (b[2] << 8)
elif b[0] & 0xc0 == 0x80: elif b[0] & 0xc0 == 0x80:
return (14, 1, (b[0] & 0x38) >> 3, b[0] & 7) return 14, 1, (b[0] & 0x38) >> 3, b[0] & 7
elif b[0] & 0xc0 == 0xc0: elif b[0] & 0xc0 == 0xc0:
return (15, 2, (b[0] & 0x38) >> 3, b[0] & 7, 0x100 if b[1] == 0 else b[1]) return 15, 2, (b[0] & 0x38) >> 3, b[0] & 7, 0x100 if b[1] == 0 else b[1]
else: else:
raise Exception('Unhandled instruction %02x' % b) raise Exception('Unhandled instruction %02x' % b)
def disassm_timeslot_table(b, off):
pc=off def disassm_timeslot_table(b: bytes, off: int):
pc = off
while len(b) > 0: while len(b) > 0:
op, sz, *operands = decode_insn(b) op, sz, *operands = decode_insn(b)
if sz > len(b): if sz > len(b):
raise Exception('Truncated instruction') raise Exception('Truncated instruction')
print(' %04x: %-6s %s' % (pc, hexlify(b[:sz]).decode(), insn_to_string[op] % tuple(operands))) print(' %04x: %-6s %s' %
(pc, hexlify(b[:sz]).decode(), insn_to_string[op] % tuple(operands)))
b = b[sz:] b = b[sz:]
pc += sz pc += sz
def find_nth_insn(b, opcode, n):
pc=0 def find_nth_insn(b: bytes, opcode: int, n: int):
pc = 0
while len(b) > 0: while len(b) > 0:
op, sz, *_ = decode_insn(b) op, sz, *_ = decode_insn(b)
@ -153,15 +157,16 @@ def find_nth_insn(b, opcode, n):
raise Exception('Truncated instruction') raise Exception('Truncated instruction')
if op == opcode: if op == opcode:
n-=1 n -= 1
if n == 0: if n == 0:
return (pc, b[:sz]) return pc, b[:sz]
b = b[sz:] b = b[sz:]
pc += sz pc += sz
def find_nth_regwrite(b, reg_addr, n):
pc=0 def find_nth_regwrite(b: bytes, reg_addr: int, n: int):
pc = 0
while len(b) > 0: while len(b) > 0:
op, sz, *operands = decode_insn(b) op, sz, *operands = decode_insn(b)
@ -171,24 +176,28 @@ def find_nth_regwrite(b, reg_addr, n):
if op == 13: if op == 13:
addr, value = operands addr, value = operands
if addr == reg_addr: if addr == reg_addr:
n-=1 n -= 1
if n == 0: if n == 0:
return (pc, b[:sz]) return pc, b[:sz]
b = b[sz:] b = b[sz:]
pc += sz pc += sz
def split_chunks(b): def split_chunks(b: bytes) -> typing.Generator[typing.List[typing.Union[int, bytes]], None, None]:
while len(b) > 0: while len(b) > 0:
(typ, sz), b = unpack('<HH', b[:4]), b[4:] (typ, sz), b = unpack('<HH', b[:4]), b[4:]
p, b = b[:sz], b[sz:] p, b = b[:sz], b[sz:]
yield [typ, p] yield [typ, p]
def merge_chunks(cs):
def merge_chunks(cs: typing.List[typing.List[typing.Union[int, bytes]]]):
return b''.join([pack('<HH', key, len(val)) + val for key, val in cs]) return b''.join([pack('<HH', key, len(val)) + val for key, val in cs])
def dump_all(b):
def dump_all(b: bytes):
ts: typing.Optional[bytes] = None
ts_off: typing.Optional[int] = None
while len(b) > 0: while len(b) > 0:
(typ, sz), b = unpack('<HH', b[:4]), b[4:] (typ, sz), b = unpack('<HH', b[:4]), b[4:]
p, b = b[:sz], b[sz:] p, b = b[:sz], b[sz:]
@ -198,19 +207,19 @@ def dump_all(b):
print(' *0x%08x = 0x%08x' % (addr, val)) print(' *0x%08x = 0x%08x' % (addr, val))
elif typ == 0x32: elif typ == 0x32:
print('Set Registers 16:') print('Set Registers 16:')
(base,), p = unpack('<L', p[:4]), p[4:] (base, ), p = unpack('<L', p[:4]), p[4:]
while len(p) > 0: while len(p) > 0:
(off, val), p = unpack('<HH', p[:4]), p[4:] (off, val), p = unpack('<HH', p[:4]), p[4:]
print(' *0x%08x = 0x%04x' % (off + base, val)) print(' *0x%08x = 0x%04x' % (off + base, val))
elif typ == 0x33: elif typ == 0x33:
print('Set Registers 32:') print('Set Registers 32:')
(base,), p = unpack('<L', p[:4]), p[4:] (base, ), p = unpack('<L', p[:4]), p[4:]
while len(p) > 0: while len(p) > 0:
(off, val), p = unpack('<HL', p[:6]), p[6:] (off, val), p = unpack('<HL', p[:6]), p[6:]
print(' *0x%08x = 0x%08x' % (off + base, val)) print(' *0x%08x = 0x%08x' % (off + base, val))
elif typ == 0x34: elif typ == 0x34:
print('%04x (%20s): (0x%x bytes) %s' % (typ, codes[typ], len(p), hexlify(p).decode())) print('%04x (%20s): (0x%x bytes) %s' % (typ, codes[typ], len(p), hexlify(p).decode()))
ts=p ts = p
elif typ == 0x29: elif typ == 0x29:
ts_off, = unpack('<L', p) ts_off, = unpack('<L', p)
print('%04x (%20s): 0x%x' % (typ, codes[typ], ts_off)) print('%04x (%20s): 0x%x' % (typ, codes[typ], ts_off))
@ -220,4 +229,3 @@ def dump_all(b):
if ts is not None: if ts is not None:
print('Timeslot table, starting at 0x%x:' % ts_off) print('Timeslot table, starting at 0x%x:' % ts_off)
disassm_timeslot_table(ts[ts_off:], ts_off) disassm_timeslot_table(ts[ts_off:], ts_off)

View file

@ -1,25 +1,25 @@
import hmac import hmac
import logging
import os import os
import pickle import pickle
import logging import typing
from binascii import hexlify, unhexlify
from hashlib import sha256
from struct import pack, unpack from struct import pack, unpack
from binascii import *
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from hashlib import sha256
from .usb import usb, Usb
from .util import unhex from .util import unhex
from .usb import usb
password_hardcoded = unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033')
gwk_sign_hardcoded = unhexlify('3a4c76b76a97981d1274247e166610e77f4d9c9d07d3c728e532916bdd28b454')
password_hardcoded=unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033') crt_hardcoded = unhex('''
gwk_sign_hardcoded=unhexlify('3a4c76b76a97981d1274247e166610e77f4d9c9d07d3c728e532916bdd28b454')
crt_hardcoded=unhex('''
170000000001000001000000fcffffffffffffffffffffff00000000000000000000000001000000fffff 170000000001000001000000fcffffffffffffffffffffff00000000000000000000000001000000fffff
fff0000000000000000000000000000000000000000000000000000000000000000000000004b60d2273e fff0000000000000000000000000000000000000000000000000000000000000000000000004b60d2273e
3cce3bf6b053ccb0061d65bc86987655bdebb3e7933aaad835c65a0000000000000000000000000000000 3cce3bf6b053ccb0061d65bc86987655bdebb3e7933aaad835c65a0000000000000000000000000000000
@ -32,56 +32,63 @@ ae6bcffffffffffffffff00000000ffffffff0000000000000000000000000000000000000000000
fff000000000000000000000000000000000000000000000000000000000000000000000000 fff000000000000000000000000000000000000000000000000000000000000000000000000
''') ''')
crypto_backend=default_backend() crypto_backend = default_backend()
def prf(secret, seed, length):
def prf(secret: bytes, seed: bytes, length: int):
n = (length + 0x20 - 1) // 0x20 n = (length + 0x20 - 1) // 0x20
res = b'' res = b''
a = hmac.new(secret, seed, sha256).digest() a = hmac.new(secret, seed, sha256).digest()
while n > 0: while n > 0:
res += hmac.new(secret, a+seed, sha256).digest() res += hmac.new(secret, a + seed, sha256).digest()
a = hmac.new(secret, a, sha256).digest() a = hmac.new(secret, a, sha256).digest()
n -= 1 n -= 1
return res[:length] return res[:length]
def hs_key(): def hs_key():
key=password_hardcoded[:0x10] key = password_hardcoded[:0x10]
seed=password_hardcoded[0x10:] + b'\xaa'*2 seed = password_hardcoded[0x10:] + b'\xaa' * 2
hs_key=prf(key, b'HS_KEY_PAIR_GEN' + seed, 0x20) hs_key = prf(key, b'HS_KEY_PAIR_GEN' + seed, 0x20)
return int(hs_key[::-1].hex(), 16) return int(hs_key[::-1].hex(), 16)
def with_2bytes_size(chunk):
def with_2bytes_size(chunk: bytes):
return pack('>H', len(chunk)) + chunk return pack('>H', len(chunk)) + chunk
def with_3bytes_size(chunk):
def with_3bytes_size(chunk: bytes):
return pack('>BH', len(chunk) >> 16, len(chunk)) + chunk return pack('>BH', len(chunk) >> 16, len(chunk)) + chunk
def with_1byte_size(chunk):
def with_1byte_size(chunk: bytes):
return pack('>B', len(chunk)) + chunk return pack('>B', len(chunk)) + chunk
def to_bytes(n): def to_bytes(n):
b=b'' b = b''
while n: while n:
b += (n & 0xff).to_bytes(1, 'big') b += (n & 0xff).to_bytes(1, 'big')
n >>= 8 n >>= 8
return b return b
def pad(b):
l = 16 - (len(b) % 16)
return b + bytes([l-1])*l
def unpad(b): def pad(b: bytes):
return b[:-1-b[-1]] l = 16 - (len(b) % 16)
return b + bytes([l - 1]) * l
def unpad(b: bytes):
return b[:-1 - b[-1]]
# TODO assert the right state transitions # TODO assert the right state transitions
class Tls(): class Tls:
def __init__(self, usb: Usb):
def __init__(self, usb):
self.usb = usb self.usb = usb
self.reset() self.reset()
try: try:
@ -101,20 +108,20 @@ class Tls():
self.secure_tx = False self.secure_tx = False
# Info about the host computer # Info about the host computer
def set_hwkey(self, product_name, serial_number): def set_hwkey(self, product_name: str, serial_number: str):
hw_key = bytes(product_name, 'ascii') + b'\0' + \ hw_key = bytes(product_name, 'ascii') + b'\0' + \
bytes(serial_number, 'ascii') + b'\0' bytes(serial_number, 'ascii') + b'\0'
# pre-TLS keys # pre-TLS keys
self.psk_encryption_key = prf(password_hardcoded, b'GWK' + hw_key, 0x20) self.psk_encryption_key = prf(password_hardcoded, b'GWK' + hw_key, 0x20)
self.psk_validation_key = prf(self.psk_encryption_key, self.psk_validation_key = prf(self.psk_encryption_key, b'GWK_SIGN' + gwk_sign_hardcoded,
b'GWK_SIGN' + gwk_sign_hardcoded, 0x20) 0x20)
def cmd(self, cmd): def cmd(self, cmd: typing.Union[bytes, typing.Callable[[], bytes]]):
if self.secure_rx and self.secure_tx: if self.secure_rx and self.secure_tx:
rsp=self.app(cmd) rsp = self.app(cmd)
else: else:
rsp=self.usb.cmd(cmd) rsp = self.usb.cmd(cmd)
return rsp return rsp
@ -124,31 +131,27 @@ class Tls():
self.handshake_hash = sha256() self.handshake_hash = sha256()
rsp=self.usb.cmd(unhexlify('44000000') + self.make_handshake(self.make_client_hello())) rsp = self.usb.cmd(unhexlify('44000000') + self.make_handshake(self.make_client_hello()))
self.parse_tls_response(rsp) self.parse_tls_response(rsp)
self.make_keys() self.make_keys()
rsp=self.usb.cmd( rsp = self.usb.cmd(
unhexlify('44000000') + unhexlify('44000000') + self.make_handshake(self.make_certs() + self.make_client_kex() +
self.make_handshake( self.make_cert_verify()) +
self.make_certs() + self.make_change_cipher_spec() + self.make_handshake(self.make_finish()))
self.make_client_kex() +
self.make_cert_verify()) +
self.make_change_cipher_spec() +
self.make_handshake(self.make_finish()))
self.parse_tls_response(rsp) self.parse_tls_response(rsp)
def trace(self, s): def trace(self, s: str):
if self.trace_enabled: if self.trace_enabled:
logging.debug(s) logging.debug(s)
def app(self, b): def app(self, b: typing.Union[bytes, typing.Callable[[], bytes]]):
b = b() if callable(b) else b b = b() if callable(b) else b
return self.parse_tls_response(self.usb.cmd(self.make_app_data(b))) return self.parse_tls_response(self.usb.cmd(self.make_app_data(b)))
def update_neg(self, b): def update_neg(self, b: bytes):
self.handshake_hash.update(b) self.handshake_hash.update(b)
def make_keys(self): def make_keys(self):
@ -156,27 +159,28 @@ class Tls():
self.session_public = skey.private_numbers().public_numbers self.session_public = skey.private_numbers().public_numbers
pre_master_secret = skey.exchange(ec.ECDH(), self.ecdh_q) pre_master_secret = skey.exchange(ec.ECDH(), self.ecdh_q)
seed = self.client_random + self.server_random seed = self.client_random + self.server_random
self.master_secret = prf(pre_master_secret, b'master secret'+seed, 0x30) self.master_secret = prf(pre_master_secret, b'master secret' + seed, 0x30)
key_block = prf(self.master_secret, b'key expansion'+seed, 0x120) key_block = prf(self.master_secret, b'key expansion' + seed, 0x120)
self.sign_key = key_block[0x00:0x20] self.sign_key = key_block[0x00:0x20]
self.validation_key = key_block[0x20:0x20+0x20] self.validation_key = key_block[0x20:0x20 + 0x20]
self.encryption_key = key_block[0x40:0x40+0x20] self.encryption_key = key_block[0x40:0x40 + 0x20]
self.decryption_key = key_block[0x60:0x60+0x20] self.decryption_key = key_block[0x60:0x60 + 0x20]
def save(self): def save(self):
with open('tls.dict', 'wb') as f: with open('tls.dict', 'wb') as f:
pickle.dump({ pickle.dump(
'sign_key': self.sign_key, {
'validation_key': self.validation_key, 'sign_key': self.sign_key,
'encryption_key': self.encryption_key, 'validation_key': self.validation_key,
'decryption_key': self.decryption_key, 'encryption_key': self.encryption_key,
'secure_rx': self.secure_rx, 'decryption_key': self.decryption_key,
'secure_tx': self.secure_tx 'secure_rx': self.secure_rx,
}, f) 'secure_tx': self.secure_tx
}, f)
def load(self): def load(self):
with open('tls.dict', 'rb') as f: with open('tls.dict', 'rb') as f:
d=pickle.load(f) d = pickle.load(f)
self.sign_key = d['sign_key'] self.sign_key = d['sign_key']
self.validation_key = d['validation_key'] self.validation_key = d['validation_key']
self.encryption_key = d['encryption_key'] self.encryption_key = d['encryption_key']
@ -184,46 +188,46 @@ class Tls():
self.secure_rx = d['secure_rx'] self.secure_rx = d['secure_rx']
self.secure_tx = d['secure_tx'] self.secure_tx = d['secure_tx']
def decrypt(self, c): def decrypt(self, c: bytes):
iv, c = c[:0x10], c[0x10:] iv, c = c[:0x10], c[0x10:]
cipher = Cipher(algorithms.AES(self.decryption_key), modes.CBC(iv), backend=crypto_backend) cipher = Cipher(algorithms.AES(self.decryption_key), modes.CBC(iv), backend=crypto_backend)
decryptor = cipher.decryptor() decryptor = cipher.decryptor()
m = decryptor.update(c) + decryptor.finalize() m = decryptor.update(c) + decryptor.finalize()
m=unpad(m) m = unpad(m)
return m return m
def encrypt(self, b): def encrypt(self, b: bytes):
#iv = unhexlify('454849acdd075174d6b9e713a957c2e7') # iv = unhexlify('454849acdd075174d6b9e713a957c2e7')
iv = os.urandom(0x10) iv = os.urandom(0x10)
cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=crypto_backend) cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=crypto_backend)
encryptor = cipher.encryptor() encryptor = cipher.encryptor()
b=pad(b) b = pad(b)
c=encryptor.update(b) + encryptor.finalize() c = encryptor.update(b) + encryptor.finalize()
return iv + c return iv + c
def validate(self, t, b): def validate(self, t: int, b: bytes):
b, hs = b[:-0x20], b[-0x20:] b, hs = b[:-0x20], b[-0x20:]
hdr = pack('>BBBH', t, 3, 3, len(b)) hdr = pack('>BBBH', t, 3, 3, len(b))
sig=hmac.new(self.validation_key, hdr+b, sha256).digest() sig = hmac.new(self.validation_key, hdr + b, sha256).digest()
if sig != hs: if sig != hs:
raise Exception('Packet signature validation check failed') raise Exception('Packet signature validation check failed')
self.trace('<tls< %02x: %s' % (t, hexlify(b).decode())) self.trace('<tls< %02x: %s' % (t, hexlify(b).decode()))
return b return b
def sign(self, t, b): def sign(self, t: int, b: bytes):
self.trace('>tls> %02x: %s' % (t, hexlify(b).decode())) self.trace('>tls> %02x: %s' % (t, hexlify(b).decode()))
hdr = pack('>BBBH', t, 3, 3, len(b)) hdr = pack('>BBBH', t, 3, 3, len(b))
sig=hmac.new(self.sign_key, hdr+b, sha256).digest() sig = hmac.new(self.sign_key, hdr + b, sha256).digest()
return b + sig return b + sig
def make_finish(self): def make_finish(self):
self.secure_tx = True self.secure_tx = True
hs_hash = self.handshake_hash.copy().digest() hs_hash = self.handshake_hash.copy().digest()
verify_data = prf(self.master_secret, b'client finished'+hs_hash, 0xc) verify_data = prf(self.master_secret, b'client finished' + hs_hash, 0xc)
return b'\x14' + with_3bytes_size(verify_data) return b'\x14' + with_3bytes_size(verify_data)
def make_change_cipher_spec(self): def make_change_cipher_spec(self):
@ -231,12 +235,13 @@ class Tls():
def make_certs(self): def make_certs(self):
cert = self.tls_cert cert = self.tls_cert
cert = unhexlify('ac16') + cert # what's this? cert = unhexlify('ac16') + cert # what's this?
cert = pack('>BH', 0, len(self.tls_cert)) + cert # this seems to violate the standard (should be len(cert)) cert = pack('>BH', 0, len(
cert = pack('>BH', 0, len(self.tls_cert)) + cert # same self.tls_cert)) + cert # this seems to violate the standard (should be len(cert))
cert = pack('>BH', 0, len(self.tls_cert)) + cert # same
return self.with_neg_hdr(0x0b, cert) return self.with_neg_hdr(0x0b, cert)
def with_neg_hdr(self, t, b): def with_neg_hdr(self, t: int, b: bytes):
b = pack('>B', t) + with_3bytes_size(b) b = pack('>B', t) + with_3bytes_size(b)
self.update_neg(b) self.update_neg(b)
return b return b
@ -246,11 +251,11 @@ class Tls():
return self.with_neg_hdr(0x10, b) return self.with_neg_hdr(0x10, b)
def make_cert_verify(self): def make_cert_verify(self):
buf=self.handshake_hash.copy().digest() buf = self.handshake_hash.copy().digest()
b=self.priv_key.sign(buf, ec.ECDSA(Prehashed(hashes.SHA256()))) b = self.priv_key.sign(buf, ec.ECDSA(Prehashed(hashes.SHA256())))
return self.with_neg_hdr(0x0f, b) return self.with_neg_hdr(0x0f, b)
def handle_server_hello(self, p): def handle_server_hello(self, p: bytes):
if p[:2] != unhexlify('0303'): if p[:2] != unhexlify('0303'):
raise Exception('unexpected TLS version %s' % hexlify(p[:2]).decode()) raise Exception('unexpected TLS version %s' % hexlify(p[:2]).decode())
@ -258,50 +263,54 @@ class Tls():
self.server_random, p = p[:0x20], p[0x20:] self.server_random, p = p[:0x20], p[0x20:]
l = p[0] l = p[0]
self.server_sessid, p = p[1:1+l], p[1+l:] self.server_sessid, p = p[1:1 + l], p[1 + l:]
(suite,), p = unpack('>H', p[:2]), p[2:] (suite, ), p = unpack('>H', p[:2]), p[2:]
if suite != 0xc005: if suite != 0xc005:
raise Exception('Server accepted unsupported cipher suite %04x' % suite) raise Exception('Server accepted unsupported cipher suite %04x' % suite)
if p[0] != 0: if p[0] != 0:
raise Exception('Server selected to enable compression, which we don''t support %02x' % p[0]) raise Exception('Server selected to enable compression, which we don'
't support %02x' % p[0])
p = p[1:] p = p[1:]
if p != b'': if p != b'':
raise Exception('Not expecting any more data') raise Exception('Not expecting any more data')
def handle_cert_req(self, p): def handle_cert_req(self, p: bytes):
(sign_and_hash_algo,), p = unpack('>H', p[:2]), p[2:] (sign_and_hash_algo, ), p = unpack('>H', p[:2]), p[2:]
if sign_and_hash_algo != 0x140: if sign_and_hash_algo != 0x140:
raise Exception('Server requested a cert with an unsupported sign and hash algo combination %04x' % sign_and_hash_algo) raise Exception(
'Server requested a cert with an unsupported sign and hash algo combination %04x' %
sign_and_hash_algo)
(l,), p = unpack('>H', p[:2]), p[2:] (l, ), p = unpack('>H', p[:2]), p[2:]
if l != 0: if l != 0:
raise Exception('Server requested a cert with non-empty list of CAs') raise Exception('Server requested a cert with non-empty list of CAs')
if p != b'': if p != b'':
raise Exception('Not expecting any more data') raise Exception('Not expecting any more data')
def handle_server_hello_done(self, p): def handle_server_hello_done(self, p: bytes):
if p != b'': if p != b'':
raise Exception('Not expecting any body for "server hello done" pkt: %s' % hexlify(p).decode()) raise Exception('Not expecting any body for "server hello done" pkt: %s' %
hexlify(p).decode())
def handle_finish(self, b): def handle_finish(self, b: bytes):
hs_hash = self.handshake_hash.copy().digest() hs_hash = self.handshake_hash.copy().digest()
verify_data = prf(self.master_secret, b'server finished'+hs_hash, 0xc) verify_data = prf(self.master_secret, b'server finished' + hs_hash, 0xc)
if verify_data != b: if verify_data != b:
raise Exception('Final handshake check failed') raise Exception('Final handshake check failed')
def handle_app_data(self, b): def handle_app_data(self, b: bytes):
if not self.secure_rx: if not self.secure_rx:
raise Exception('App payload before secure connection established') raise Exception('App payload before secure connection established')
return self.validate(0x17, self.decrypt(b)) return self.validate(0x17, self.decrypt(b))
def handle_handshake(self, handshake): def handle_handshake(self, handshake: bytes) -> None:
if self.secure_rx: if self.secure_rx:
handshake = self.validate(0x16, self.decrypt(handshake)) handshake = self.validate(0x16, self.decrypt(handshake))
@ -325,10 +334,10 @@ class Tls():
else: else:
raise Exception('Unknown handshake packet %02x' % t) raise Exception('Unknown handshake packet %02x' % t)
self.update_neg(hdr+p) self.update_neg(hdr + p)
def parse_tls_response(self, rsp): def parse_tls_response(self, rsp: bytes):
app_data=b'' app_data = b''
while len(rsp) > 0: while len(rsp) > 0:
while len(rsp) < 5: while len(rsp) < 5:
@ -347,7 +356,7 @@ class Tls():
elif t == 0x14: elif t == 0x14:
if pkt != unhexlify('01'): if pkt != unhexlify('01'):
raise Exception('Unexpected ChangeCipherSpec payload') raise Exception('Unexpected ChangeCipherSpec payload')
self.secure_rx = True self.secure_rx = True
elif t == 0x17: elif t == 0x17:
@ -358,47 +367,48 @@ class Tls():
return app_data return app_data
def make_app_data(self, b): def make_app_data(self, b: bytes):
if not self.secure_tx: if not self.secure_tx:
raise Exception('App payload before secure connection established') raise Exception('App payload before secure connection established')
b=self.encrypt(self.sign(0x17, b)) b = self.encrypt(self.sign(0x17, b))
return unhexlify('170303') + with_2bytes_size(b) return unhexlify('170303') + with_2bytes_size(b)
def make_handshake(self, b): def make_handshake(self, b: bytes):
if self.secure_tx: if self.secure_tx:
b=self.encrypt(self.sign(0x16, b)) b = self.encrypt(self.sign(0x16, b))
return unhexlify('160303') + with_2bytes_size(b) return unhexlify('160303') + with_2bytes_size(b)
def make_client_hello(self): def make_client_hello(self):
h = unhexlify('0303') # TLS 1.2 h = unhexlify('0303') # TLS 1.2
#self.client_random = unhexlify('bc349559ac16c8f8362191395b4d04a435d870315f519eed8777488bc2b9600c') # self.client_random = unhexlify('bc349559ac16c8f8362191395b4d04a435d870315f519eed8777488bc2b9600c')
self.client_random = os.urandom(0x20) self.client_random = os.urandom(0x20)
h += self.client_random # client's random h += self.client_random # client's random
h += with_1byte_size(unhexlify('00000000000000')) # session ID h += with_1byte_size(unhexlify('00000000000000')) # session ID
suits = b'' suits = b''
suits += pack('>H', 0xc005) # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA suits += pack('>H', 0xc005) # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
suits += pack('>H', 0x003d) # TLS_RSA_WITH_AES_256_CBC_SHA256 suits += pack('>H', 0x003d) # TLS_RSA_WITH_AES_256_CBC_SHA256
suits += pack('>H', 0x008d) # TLS_RSA_WITH_AES_256_CBC_SHA256 suits += pack('>H', 0x008d) # TLS_RSA_WITH_AES_256_CBC_SHA256
h += with_2bytes_size(suits) h += with_2bytes_size(suits)
h += with_1byte_size(b'') # no compression options h += with_1byte_size(b'') # no compression options
exts = b'' exts = b''
exts += self.make_ext(0x004, pack('>H', 0x0017)) # truncated_hmac = 0x17 exts += self.make_ext(0x004, pack('>H', 0x0017)) # truncated_hmac = 0x17
exts += self.make_ext(0x00b, with_1byte_size(unhexlify('00'))) # EC points format = uncompressed exts += self.make_ext(0x00b,
with_1byte_size(unhexlify('00'))) # EC points format = uncompressed
# h += with_2bytes_size(exts) # h += with_2bytes_size(exts)
h += pack('>H', len(exts)-2) + exts # -2? WHY?!... h += pack('>H', len(exts) - 2) + exts # -2? WHY?!...
return self.with_neg_hdr(0x01, h) return self.with_neg_hdr(0x01, h)
def make_ext(self, id, b): def make_ext(self, id: int, b: bytes):
return pack('>H', id) + with_2bytes_size(b) return pack('>H', id) + with_2bytes_size(b)
def parseTlsFlash(self, reply): def parse_tls_flash(self, reply: bytes):
while len(reply) > 0: while len(reply) > 0:
hdr, reply = reply[:4], reply[4:] hdr, reply = reply[:4], reply[4:]
hs, reply = reply[:0x20], reply[0x20:] hs, reply = reply[:0x20], reply[0x20:]
@ -410,7 +420,7 @@ class Tls():
self.trace('block id %04x (%d bytes)' % (id, sz)) self.trace('block id %04x (%d bytes)' % (id, sz))
m=sha256() m = sha256()
m.update(body) m.update(body)
if m.digest() != hs: if m.digest() != hs:
@ -431,37 +441,37 @@ class Tls():
else: else:
self.trace('unhandled block id %04x (%d bytes): %s' % (id, sz, hexlify(body))) self.trace('unhandled block id %04x (%d bytes): %s' % (id, sz, hexlify(body)))
def makeTlsFlashBlock(self, id, body): def make_tls_flash_block(self, id: int, body: bytes):
m=sha256() m = sha256()
m.update(body) m.update(body)
hdr = pack('<HH', id, len(body)) hdr = pack('<HH', id, len(body))
return hdr+m.digest()+body return hdr + m.digest() + body
def makeTlsFlash(self): def make_tls_flash(self):
b = self.makeTlsFlashBlock(0, b'\0') b = self.make_tls_flash_block(0, b'\0')
b+= self.makeTlsFlashBlock(4, self.priv_blob) b += self.make_tls_flash_block(4, self.priv_blob)
b+= self.makeTlsFlashBlock(3, self.tls_cert) b += self.make_tls_flash_block(3, self.tls_cert)
b+= self.makeTlsFlashBlock(5, crt_hardcoded) b += self.make_tls_flash_block(5, crt_hardcoded)
b+= self.makeTlsFlashBlock(1, b'\0' * 0x100) b += self.make_tls_flash_block(1, b'\0' * 0x100)
b+= self.makeTlsFlashBlock(2, b'\0' * 0x100) b += self.make_tls_flash_block(2, b'\0' * 0x100)
b+= self.makeTlsFlashBlock(6, self.ecdh_blob) b += self.make_tls_flash_block(6, self.ecdh_blob)
b+= b'\xff' * (0x1000 - len(b)) b += b'\xff' * (0x1000 - len(b))
return b return b
def handle_empty(self, body): def handle_empty(self, body: bytes):
if body != b'\0' * len(body): if body != b'\0' * len(body):
raise Exception('Expected empty block') raise Exception('Expected empty block')
def handle_cert(self, body): def handle_cert(self, body: bytes):
# TODO validate cert, check if pub keys match # TODO validate cert, check if pub keys match
self.tls_cert = body self.tls_cert = body
self.trace('TLS cert blob: %s' % hexlify(self.tls_cert)) self.trace('TLS cert blob: %s' % hexlify(self.tls_cert))
def handle_ecdh(self, body): def handle_ecdh(self, body: bytes):
self.ecdh_blob = body self.ecdh_blob = body
key, signature = body[:0x90], body[0x90:] key, signature = body[:0x90], body[0x90:]
x = key[0x8:0x8+0x20] x = key[0x8:0x8 + 0x20]
y = key[0x4c:0x4c+0x20] y = key[0x4c:0x4c + 0x20]
x, y = [int(hexlify(i[::-1]), 0x10) for i in [x, y]] x, y = [int(hexlify(i[::-1]), 0x10) for i in [x, y]]
@ -477,35 +487,39 @@ class Tls():
l, = unpack('<L', l) l, = unpack('<L', l)
signature, zeroes = signature[:l], signature[l:] signature, zeroes = signature[:l], signature[l:]
if zeroes != b'\0'*len(zeroes): if zeroes != b'\0' * len(zeroes):
raise Exception('Zeroes expected') raise Exception('Zeroes expected')
# The following pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll. # The following pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll.
# Corresponding private key should only be known to a genuine Synaptic device. # Corresponding private key should only be known to a genuine Synaptic device.
fwpub = ec.EllipticCurvePublicNumbers( fwpub = ec.EllipticCurvePublicNumbers(
0xf727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3, 0xf727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3,
0xa85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94, ec.SECP256R1()).public_key(crypto_backend) 0xa85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94,
ec.SECP256R1()).public_key(crypto_backend)
# throws InvalidSignature # throws InvalidSignature
fwpub.verify(signature, key, ec.ECDSA(hashes.SHA256())) fwpub.verify(signature, key, ec.ECDSA(hashes.SHA256()))
def handle_priv(self, body: bytes):
def handle_priv(self, body):
self.priv_blob = body self.priv_blob = body
prefix, body = body[0], body[1:] prefix, body = body[0], body[1:]
if prefix != 2: if prefix != 2:
raise Exception('Unknown private key prefix %02x' % prefix) raise Exception('Unknown private key prefix %02x' % prefix)
c, hs = body[:-0x20], body[-0x20:] c, hs = body[:-0x20], body[-0x20:]
sig=hmac.new(self.psk_validation_key, c, sha256).digest() sig = hmac.new(self.psk_validation_key, c, sha256).digest()
if hs != sig: if hs != sig:
raise Exception('Signature verification failed. This device was probably paired with another computer.') raise Exception(
'Signature verification failed. This device was probably paired with another computer.'
)
iv, c = c[:0x10], c[0x10:] iv, c = c[:0x10], c[0x10:]
cipher = Cipher(algorithms.AES(self.psk_encryption_key), modes.CBC(iv), backend=crypto_backend) cipher = Cipher(algorithms.AES(self.psk_encryption_key),
modes.CBC(iv),
backend=crypto_backend)
decryptor = cipher.decryptor() decryptor = cipher.decryptor()
m = decryptor.update(c) + decryptor.finalize() m = decryptor.update(c) + decryptor.finalize()
m=m[:-m[-1]] # unpad (standard this time) m = m[:-m[-1]] # unpad (standard this time)
x, m = m[:0x20], m[0x20:] x, m = m[:0x20], m[0x20:]
y, m = m[:0x20], m[0x20:] y, m = m[:0x20], m[0x20:]
@ -519,8 +533,8 @@ class Tls():
self.trace('d=0x%x' % d) self.trace('d=0x%x' % d)
# Someone has reported that x and y are 0 after pairing with the latest windows driver. # Someone has reported that x and y are 0 after pairing with the latest windows driver.
#pub_key = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()) # pub_key = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1())
#self.priv_key = ec.EllipticCurvePrivateNumbers(d, pub_key).private_key(crypto_backend) # self.priv_key = ec.EllipticCurvePrivateNumbers(d, pub_key).private_key(crypto_backend)
self.priv_key = ec.derive_private_key(d, ec.SECP256R1(), backend=crypto_backend) self.priv_key = ec.derive_private_key(d, ec.SECP256R1(), backend=crypto_backend)

View file

@ -1,12 +1,14 @@
from time import ctime
import logging import logging
import typing
from os.path import basename from os.path import basename
from time import ctime
from .usb import usb
from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
from .flash import write_flash_all, write_fw_signature, get_fw_info from .flash import write_flash_all, write_fw_signature, get_fw_info
from .sensor import reboot, write_hw_reg32, read_hw_reg32, identify_sensor
from .usb import usb
firmware_home = '/usr/share/python-validity'
firmware_home='/usr/share/python-validity'
def default_fwext_name(): def default_fwext_name():
if usb.usb_dev().idVendor == 0x138a: if usb.usb_dev().idVendor == 0x138a:
@ -14,15 +16,17 @@ def default_fwext_name():
return '6_07f_Lenovo.xpfwext' return '6_07f_Lenovo.xpfwext'
# It looks like the firmware file must match DLL, not the hardware. # It looks like the firmware file must match DLL, not the hardware.
# Both DLL and xpfwext are universal. # Both DLL and xpfwext are universal.
# The device dependant code seems to be loaded dynamically (via encrypted blobs). # The device dependant code seems to be loaded dynamically (via encrypted blobs).
# So, it is important that xpfwext file is matching the blobs contents. # So, it is important that xpfwext file is matching the blobs contents.
return '6_07f_lenovo_mis_qm.xpfwext' return '6_07f_lenovo_mis_qm.xpfwext'
def upload_fwext(fw_path=None):
fwi=get_fw_info(2) def upload_fwext(fw_path: typing.Optional[str] = None):
if fwi != None: fwi = get_fw_info(2)
logging.info('Detected firmware version %d.%d (%s))' % (fwi.major, fwi.minor, ctime(fwi.buildtime))) if fwi is not None:
logging.info('Detected firmware version %d.%d (%s))' %
(fwi.major, fwi.minor, ctime(fwi.buildtime)))
return return
else: else:
logging.info('No firmware detected. Uploading...') logging.info('No firmware detected. Uploading...')
@ -32,33 +36,32 @@ def upload_fwext(fw_path=None):
if read_hw_reg32(0x80002080) not in [2, 3]: if read_hw_reg32(0x80002080) not in [2, 3]:
raise Exception('Unexpected register value') raise Exception('Unexpected register value')
dev=identify_sensor() dev = identify_sensor()
logging.debug('Sensor: %s' % dev.name) logging.debug('Sensor: %s' % dev.name)
# ^ TODO -- what is the real reason to detect HW at this stage? # ^ TODO -- what is the real reason to detect HW at this stage?
# just a guess: perhaps it is used to construct fwext filename # just a guess: perhaps it is used to construct fwext filename
default_name = firmware_home + '/' + default_fwext_name() default_name = firmware_home + '/' + default_fwext_name()
if not fw_path: if not fw_path:
fw_path = default_name fw_path = default_name
elif basename(fw_path) != default_name: elif basename(fw_path) != default_name:
logging.warning('WARNING: Your device fw is supposed to be called {}'.format( logging.warning('WARNING: Your device fw is supposed to be called {}'.format(default_name))
default_name))
with open(fw_path, 'rb') as f: with open(fw_path, 'rb') as f:
fwext=f.read() fwext = f.read()
fwext=fwext[fwext.index(b'\x1a')+1:] fwext = fwext[fwext.index(b'\x1a') + 1:]
fwext, signature = fwext[:-0x100], fwext[-0x100:] fwext, signature = fwext[:-0x100], fwext[-0x100:]
write_flash_all(2, 0, fwext) write_flash_all(2, 0, fwext)
write_fw_signature(2, signature) write_fw_signature(2, signature)
fwi=get_fw_info(2) fwi = get_fw_info(2)
if fwi == None: if fwi is None:
raise Exception('No firmware detected') raise Exception('No firmware detected')
logging.info('Loaded FWExt version %d.%d (%s), %d modules' % (fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules))) logging.info('Loaded FWExt version %d.%d (%s), %d modules' %
(fwi.major, fwi.minor, ctime(fwi.buildtime), len(fwi.modules)))
# Reboot # Reboot
reboot() reboot()

View file

@ -1,33 +1,37 @@
import logging
import errno import errno
import usb.core as ucore import logging
from binascii import * import typing
from .util import assert_status from binascii import hexlify, unhexlify
from struct import unpack from struct import unpack
from usb.core import USBError
from .blobs import init_hardcoded, init_hardcoded_clean_slate
supported_devices=[ import usb.core as ucore
from usb.core import USBError
from .blobs import init_hardcoded, init_hardcoded_clean_slate
from .util import assert_status
supported_devices = [
(0x138a, 0x0090), (0x138a, 0x0090),
(0x138a, 0x0097), (0x138a, 0x0097),
(0x06cb, 0x009a), (0x06cb, 0x009a),
] ]
class CancelledException(Exception): class CancelledException(Exception):
pass pass
class Usb():
class Usb:
def __init__(self): def __init__(self):
self.interrupt_cb = None
self.trace_enabled = False self.trace_enabled = False
self.dev = None self.dev: typing.Optional[ucore.Device] = None
self.cancel = False self.cancel = False
def open(self, vendor=None, product=None): def open(self, vendor=None, product=None):
if vendor is not None and product is not None: if vendor is not None and product is not None:
dev = ucore.find(idVendor=vendor, idProduct=product) dev = ucore.find(idVendor=vendor, idProduct=product)
else: else:
def match(d): def match(d):
return (d.idVendor, d.idProduct) in supported_devices return (d.idVendor, d.idProduct) in supported_devices
@ -35,15 +39,15 @@ class Usb():
self.open_dev(dev) self.open_dev(dev)
def open_devpath(self, busnum, address): def open_devpath(self, busnum: int, address: int):
def match(d): def match(d):
return d.bus == busnum and d.address == address return d.bus == busnum and d.address == address
dev = ucore.find(custom_match=match) dev = ucore.find(custom_match=match)
self.open_dev(dev) self.open_dev(dev)
def open_dev(self, dev): def open_dev(self, dev: ucore.Device):
if dev is None: if dev is None:
raise Exception('No matching devices found') raise Exception('No matching devices found')
@ -62,39 +66,39 @@ class Usb():
return self.dev return self.dev
def send_init(self): def send_init(self):
#self.dev.set_configuration() # self.dev.set_configuration()
# TODO analyse responses, detect hardware type # TODO analyse responses, detect hardware type
assert_status(self.cmd(unhexlify('01'))) # RomInfo.get() assert_status(self.cmd(unhexlify('01'))) # RomInfo.get()
assert_status(self.cmd(unhexlify('19'))) assert_status(self.cmd(unhexlify('19')))
# 43 -- get partition header(?) (02 -- fwext partition) # 43 -- get partition header(?) (02 -- fwext partition)
# c28c745a in response is a FwextBuildtime = 0x5A748CC2 # c28c745a in response is a FwextBuildtime = 0x5A748CC2
rsp=self.cmd(unhexlify('4302')) # get_fw_info() rsp = self.cmd(unhexlify('4302')) # get_fw_info()
assert_status(self.cmd(init_hardcoded)) assert_status(self.cmd(init_hardcoded))
(err,), rsp = unpack('<H', rsp[:2]), rsp[2:] (err, ), rsp = unpack('<H', rsp[:2]), rsp[2:]
if err != 0: if err != 0:
# fwext is not loaded # fwext is not loaded
logging.info('Clean slate') logging.info('Clean slate')
self.cmd(init_hardcoded_clean_slate) self.cmd(init_hardcoded_clean_slate)
def cmd(self, out): def cmd(self, out: typing.Union[bytes, typing.Callable[[], bytes]]):
if callable(out): if callable(out):
out = out() out = out()
if not out: if not out:
return 0 return 0
self.trace('>cmd> %s' % hexlify(out).decode()) self.trace('>cmd> %s' % hexlify(out).decode())
self.dev.write(1, out) self.dev.write(1, out)
resp = self.dev.read(129, 100*1024) resp = self.dev.read(129, 100 * 1024)
resp = bytes(resp) resp = bytes(resp)
self.trace('<cmd< %s' % hexlify(resp).decode()) self.trace('<cmd< %s' % hexlify(resp).decode())
return resp return resp
def read_82(self): def read_82(self):
try: try:
resp = self.dev.read(130, 1024*1024, timeout=10000) resp = self.dev.read(130, 1024 * 1024, timeout=10000)
resp = bytes(resp) resp = bytes(resp)
self.trace('<130< %s' % hexlify(resp).decode()) self.trace('<130< %s' % hexlify(resp).decode())
return resp return resp
@ -122,8 +126,9 @@ class Usb():
else: else:
raise e raise e
def trace(self, s): def trace(self, s: str):
if self.trace_enabled: if self.trace_enabled:
logging.debug(s) logging.debug(s)
usb=Usb()
usb = Usb()

View file

@ -1,10 +1,10 @@
import re import re
from struct import unpack
from binascii import unhexlify from binascii import unhexlify
from struct import unpack
def assert_status(b):
s,=unpack('<H', b[:2]) def assert_status(b: bytes):
s, = unpack('<H', b[:2])
if s != 0: if s != 0:
if s == 0x44f: if s == 0x44f:
raise Exception('Signature validation failed: %04x' % s) raise Exception('Signature validation failed: %04x' % s)
@ -12,6 +12,5 @@ def assert_status(b):
raise Exception('Failed: %04x' % s) raise Exception('Failed: %04x' % s)
def unhex(x): def unhex(x: str):
return unhexlify(re.sub('\W', '', x)) return unhexlify(re.sub(r'\W', '', x))

View file

@ -1,36 +1,34 @@
finger_ids = { finger_ids = {
# https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/winbio_types.h#L864-L878 # https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/winbio_types.h#L864-L878
"WINBIO_ANSI_381_POS_UNKNOWN": 0, "WINBIO_ANSI_381_POS_UNKNOWN": 0,
"WINBIO_ANSI_381_POS_RH_THUMB": 1, "WINBIO_ANSI_381_POS_RH_THUMB": 1,
"WINBIO_ANSI_381_POS_RH_INDEX_FINGER": 2, "WINBIO_ANSI_381_POS_RH_INDEX_FINGER": 2,
"WINBIO_ANSI_381_POS_RH_MIDDLE_FINGER": 3, "WINBIO_ANSI_381_POS_RH_MIDDLE_FINGER": 3,
"WINBIO_ANSI_381_POS_RH_RING_FINGER": 4, "WINBIO_ANSI_381_POS_RH_RING_FINGER": 4,
"WINBIO_ANSI_381_POS_RH_LITTLE_FINGER": 5, "WINBIO_ANSI_381_POS_RH_LITTLE_FINGER": 5,
"WINBIO_ANSI_381_POS_LH_THUMB": 6, "WINBIO_ANSI_381_POS_LH_THUMB": 6,
"WINBIO_ANSI_381_POS_LH_INDEX_FINGER": 7, "WINBIO_ANSI_381_POS_LH_INDEX_FINGER": 7,
"WINBIO_ANSI_381_POS_LH_MIDDLE_FINGER": 8, "WINBIO_ANSI_381_POS_LH_MIDDLE_FINGER": 8,
"WINBIO_ANSI_381_POS_LH_RING_FINGER": 9, "WINBIO_ANSI_381_POS_LH_RING_FINGER": 9,
"WINBIO_ANSI_381_POS_LH_LITTLE_FINGER": 10, "WINBIO_ANSI_381_POS_LH_LITTLE_FINGER": 10,
"WINBIO_ANSI_381_POS_RH_FOUR_FINGERS": 13,
"WINBIO_ANSI_381_POS_LH_FOUR_FINGERS": 14,
"WINBIO_ANSI_381_POS_TWO_THUMBS": 15,
"WINBIO_ANSI_381_POS_RH_FOUR_FINGERS": 13, # https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/winbio_types.h#L920-L929
"WINBIO_ANSI_381_POS_LH_FOUR_FINGERS": 14, "WINBIO_FINGER_UNSPECIFIED_POS_01": 0xf5,
"WINBIO_ANSI_381_POS_TWO_THUMBS": 15, "WINBIO_FINGER_UNSPECIFIED_POS_02": 0xf6,
"WINBIO_FINGER_UNSPECIFIED_POS_03": 0xf7,
"WINBIO_FINGER_UNSPECIFIED_POS_04": 0xf8,
# https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/winbio_types.h#L920-L929 "WINBIO_FINGER_UNSPECIFIED_POS_05": 0xf9,
"WINBIO_FINGER_UNSPECIFIED_POS_01": 0xf5, "WINBIO_FINGER_UNSPECIFIED_POS_06": 0xfa,
"WINBIO_FINGER_UNSPECIFIED_POS_02": 0xf6, "WINBIO_FINGER_UNSPECIFIED_POS_07": 0xfb,
"WINBIO_FINGER_UNSPECIFIED_POS_03": 0xf7, "WINBIO_FINGER_UNSPECIFIED_POS_08": 0xfc,
"WINBIO_FINGER_UNSPECIFIED_POS_04": 0xf8, "WINBIO_FINGER_UNSPECIFIED_POS_09": 0xfd,
"WINBIO_FINGER_UNSPECIFIED_POS_05": 0xf9, "WINBIO_FINGER_UNSPECIFIED_POS_10": 0xfe
"WINBIO_FINGER_UNSPECIFIED_POS_06": 0xfa,
"WINBIO_FINGER_UNSPECIFIED_POS_07": 0xfb,
"WINBIO_FINGER_UNSPECIFIED_POS_08": 0xfc,
"WINBIO_FINGER_UNSPECIFIED_POS_09": 0xfd,
"WINBIO_FINGER_UNSPECIFIED_POS_10": 0xfe
} }
# Store the keys in the reverse order for faster lookups # Store the keys in the reverse order for faster lookups
finger_names = {} finger_names = {}
for name, index in finger_ids.items(): for name, index in finger_ids.items():
finger_names[index] = name finger_names[index] = name