xref: /btstack/test/mesh/simulator.py (revision 6ccd8248590f666db07dd7add13fecb4f5664fb5)
1*6ccd8248SMilanka Ringwald#!/usr/bin/env python3
21fbe4564SMatthias Ringwald#
31fbe4564SMatthias Ringwald# Simulate network of Bluetooth Controllers
41fbe4564SMatthias Ringwald#
51fbe4564SMatthias Ringwald# Each simulated controller has an HCI H4 interface
61fbe4564SMatthias Ringwald# Network configuration will be stored in a YAML file or similar
71fbe4564SMatthias Ringwald#
81fbe4564SMatthias Ringwald# Copyright 2017 BlueKitchen GmbH
91fbe4564SMatthias Ringwald#
101fbe4564SMatthias Ringwald
111fbe4564SMatthias Ringwald
121fbe4564SMatthias Ringwaldimport os
131fbe4564SMatthias Ringwaldimport pty
141fbe4564SMatthias Ringwaldimport select
151fbe4564SMatthias Ringwaldimport subprocess
161fbe4564SMatthias Ringwaldimport sys
171fbe4564SMatthias Ringwaldimport bisect
181fbe4564SMatthias Ringwaldimport time
191fbe4564SMatthias Ringwald
20*6ccd8248SMilanka Ringwald# fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library
21*6ccd8248SMilanka Ringwaldtry:
22*6ccd8248SMilanka Ringwald    from Cryptodome.Cipher import AES
23*6ccd8248SMilanka Ringwald    import Cryptodome.Random as Random
24*6ccd8248SMilanka Ringwaldexcept ImportError:
25*6ccd8248SMilanka Ringwald    # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library
26*6ccd8248SMilanka Ringwald    try:
271fbe4564SMatthias Ringwald        from Crypto.Cipher import AES
28*6ccd8248SMilanka Ringwald        import Crypto.Random as Random
29*6ccd8248SMilanka Ringwald    except ImportError:
30*6ccd8248SMilanka Ringwald        print("\n[!] PyCryptodome required but not installed (using random value instead)")
31*6ccd8248SMilanka Ringwald        print("[!] Please install PyCryptodome, e.g. 'pip3 install pycryptodomex' or 'pip3 install pycryptodome'\n")
32*6ccd8248SMilanka Ringwald
331fbe4564SMatthias Ringwald
341fbe4564SMatthias Ringwalddef little_endian_read_16(buffer, pos):
351fbe4564SMatthias Ringwald    return ord(buffer[pos]) + (ord(buffer[pos+1]) << 8)
361fbe4564SMatthias Ringwald
371fbe4564SMatthias Ringwalddef as_hex(data):
381fbe4564SMatthias Ringwald    str_list = []
391fbe4564SMatthias Ringwald    for byte in data:
401fbe4564SMatthias Ringwald        str_list.append("{0:02x} ".format(ord(byte)))
411fbe4564SMatthias Ringwald    return ''.join(str_list)
421fbe4564SMatthias Ringwald
431fbe4564SMatthias Ringwaldadv_type_names = ['ADV_IND', 'ADV_DIRECT_IND_HIGH', 'ADV_SCAN_IND', 'ADV_NONCONN_IND', 'ADV_DIRECT_IND_LOW']
441fbe4564SMatthias Ringwaldtimers_timeouts = []
451fbe4564SMatthias Ringwaldtimers_callbacks = []
461fbe4564SMatthias Ringwald
471fbe4564SMatthias Ringwaldclass H4Parser:
481fbe4564SMatthias Ringwald
491fbe4564SMatthias Ringwald    def __init__(self):
501fbe4564SMatthias Ringwald        self.packet_type = "NONE"
511fbe4564SMatthias Ringwald        self.reset()
521fbe4564SMatthias Ringwald
531fbe4564SMatthias Ringwald    def set_packet_handler(self, handler):
541fbe4564SMatthias Ringwald        self.handler = handler
551fbe4564SMatthias Ringwald
561fbe4564SMatthias Ringwald    def reset(self):
571fbe4564SMatthias Ringwald        self.bytes_to_read = 1
581fbe4564SMatthias Ringwald        self.buffer = ''
591fbe4564SMatthias Ringwald        self.state = "H4_W4_PACKET_TYPE"
601fbe4564SMatthias Ringwald
611fbe4564SMatthias Ringwald    def parse(self, data):
621fbe4564SMatthias Ringwald        self.buffer += data
631fbe4564SMatthias Ringwald        self.bytes_to_read -= 1
641fbe4564SMatthias Ringwald        if self.bytes_to_read == 0:
651fbe4564SMatthias Ringwald            if self.state == "H4_W4_PACKET_TYPE":
661fbe4564SMatthias Ringwald                self.buffer = ''
671fbe4564SMatthias Ringwald                if data == chr(1):
681fbe4564SMatthias Ringwald                    # cmd
691fbe4564SMatthias Ringwald                    self.packet_type = "CMD"
701fbe4564SMatthias Ringwald                    self.state = "W4_CMD_HEADER"
711fbe4564SMatthias Ringwald                    self.bytes_to_read = 3
721fbe4564SMatthias Ringwald                if data == chr(2):
731fbe4564SMatthias Ringwald                    # acl
741fbe4564SMatthias Ringwald                    self.packet_type = "ACL"
751fbe4564SMatthias Ringwald                    self.state = "W4_ACL_HEADER"
761fbe4564SMatthias Ringwald                    self.bytes_to_read = 4
771fbe4564SMatthias Ringwald                return
781fbe4564SMatthias Ringwald            if self.state == "W4_CMD_HEADER":
791fbe4564SMatthias Ringwald                self.bytes_to_read = ord(self.buffer[2])
801fbe4564SMatthias Ringwald                self.state = "H4_W4_PAYLOAD"
811fbe4564SMatthias Ringwald                if self.bytes_to_read > 0:
821fbe4564SMatthias Ringwald                    return
831fbe4564SMatthias Ringwald                # fall through to handle payload len = 0
841fbe4564SMatthias Ringwald            if self.state == "W4_ACL_HEADER":
851fbe4564SMatthias Ringwald                self.bytes_to_read = little_endian_read_16(buffer, 2)
861fbe4564SMatthias Ringwald                self.state = "H4_W4_PAYLOAD"
871fbe4564SMatthias Ringwald                if self.bytes_to_read > 0:
881fbe4564SMatthias Ringwald                    return
891fbe4564SMatthias Ringwald                # fall through to handle payload len = 0
901fbe4564SMatthias Ringwald            if self.state == "H4_W4_PAYLOAD":
911fbe4564SMatthias Ringwald                self.handler(self.packet_type, self.buffer)
921fbe4564SMatthias Ringwald                self.reset()
931fbe4564SMatthias Ringwald                return
941fbe4564SMatthias Ringwald
951fbe4564SMatthias Ringwaldclass HCIController:
961fbe4564SMatthias Ringwald
971fbe4564SMatthias Ringwald    def __init__(self):
981fbe4564SMatthias Ringwald        self.fd = -1
991fbe4564SMatthias Ringwald        self.random = Random.new()
1001fbe4564SMatthias Ringwald        self.name = 'BTstack Mesh Simulator'
1011fbe4564SMatthias Ringwald        self.bd_addr = 'aaaaaa'
1021fbe4564SMatthias Ringwald        self.parser = H4Parser()
1031fbe4564SMatthias Ringwald        self.parser.set_packet_handler(self.packet_handler)
1041fbe4564SMatthias Ringwald        self.adv_enabled = 0
1051fbe4564SMatthias Ringwald        self.adv_type = 0
1061fbe4564SMatthias Ringwald        self.adv_interval_min = 0
1071fbe4564SMatthias Ringwald        self.adv_interval_max = 0
1081fbe4564SMatthias Ringwald        self.adv_data = ''
1091fbe4564SMatthias Ringwald        self.scan_enabled = False
1101fbe4564SMatthias Ringwald
1111fbe4564SMatthias Ringwald    def parse(self, data):
1121fbe4564SMatthias Ringwald        self.parser.parse(data)
1131fbe4564SMatthias Ringwald
1141fbe4564SMatthias Ringwald    def set_fd(self,fd):
1151fbe4564SMatthias Ringwald        self.fd = fd
1161fbe4564SMatthias Ringwald
1171fbe4564SMatthias Ringwald    def set_bd_addr(self, bd_addr):
1181fbe4564SMatthias Ringwald        self.bd_addr = bd_addr
1191fbe4564SMatthias Ringwald
1201fbe4564SMatthias Ringwald    def set_name(self, name):
1211fbe4564SMatthias Ringwald        self.name = name
1221fbe4564SMatthias Ringwald
1231fbe4564SMatthias Ringwald    def set_adv_handler(self, adv_handler, adv_handler_context):
1241fbe4564SMatthias Ringwald        self.adv_handler         = adv_handler
1251fbe4564SMatthias Ringwald        self.adv_handler_context = adv_handler_context
1261fbe4564SMatthias Ringwald
1271fbe4564SMatthias Ringwald    def is_scanning(self):
1281fbe4564SMatthias Ringwald        return self.scan_enabled
1291fbe4564SMatthias Ringwald
1301fbe4564SMatthias Ringwald    def emit_command_complete(self, opcode, result):
1311fbe4564SMatthias Ringwald        # type, event, len, num commands, opcode, result
1321fbe4564SMatthias Ringwald        os.write(self.fd, '\x04\x0e' + chr(3 + len(result)) + chr(1) + chr(opcode & 255)  + chr(opcode >> 8) + result)
1331fbe4564SMatthias Ringwald
1341fbe4564SMatthias Ringwald    def emit_adv_report(self, event_type, rssi):
1351fbe4564SMatthias Ringwald        # type, event, len, Subevent_Code, Num_Reports, Event_Type[i], Address_Type[i], Address[i], Length[i], Data[i], RSSI[i]
1361fbe4564SMatthias Ringwald        event = '\x04\x3e' + chr(12 + len(self.adv_data)) + chr(2) + chr(1) + chr(event_type) + chr(0) + self.bd_addr[::-1] + chr(len(self.adv_data)) + self.adv_data + chr(rssi)
1371fbe4564SMatthias Ringwald        self.adv_handler(self.adv_handler_context, event)
1381fbe4564SMatthias Ringwald
1391fbe4564SMatthias Ringwald    def handle_set_adv_enable(self, enable):
1401fbe4564SMatthias Ringwald        self.adv_enabled = enable
1411fbe4564SMatthias Ringwald        print('Node %s adv enable %u' % (self.name, self.adv_enabled))
1421fbe4564SMatthias Ringwald        if self.adv_enabled:
1431fbe4564SMatthias Ringwald            add_timer(1, self.handle_adv_timer, self)
1441fbe4564SMatthias Ringwald        else:
1451fbe4564SMatthias Ringwald            remove_timer(self.handle_adv_timer, self)
1461fbe4564SMatthias Ringwald
1471fbe4564SMatthias Ringwald    def handle_set_adv_data(self, data):
1481fbe4564SMatthias Ringwald        self.adv_data = data
1491fbe4564SMatthias Ringwald        print('Node %s adv data %s' % (self.name, as_hex(self.adv_data)))
1501fbe4564SMatthias Ringwald
1511fbe4564SMatthias Ringwald    def handle_set_adv_params(self, interval_min, interval_max, adv_type):
1521fbe4564SMatthias Ringwald        self.adv_interval_min = interval_min * 0.625
1531fbe4564SMatthias Ringwald        self.adv_interval_max = interval_max * 0.625
1541fbe4564SMatthias Ringwald        self.adv_type         = adv_type
1551fbe4564SMatthias Ringwald        print('Node %s adv interval min/max %u/%u ms, type %s' % (self.name, self.adv_interval_min, self.adv_interval_max, adv_type_names[self.adv_type]))
1561fbe4564SMatthias Ringwald
1571fbe4564SMatthias Ringwald    def handle_adv_timer(self, context):
1581fbe4564SMatthias Ringwald        if self.adv_enabled:
1591fbe4564SMatthias Ringwald            self.emit_adv_report(0, 0)
1601fbe4564SMatthias Ringwald            add_timer(self.adv_interval_min, self.handle_adv_timer, self)
1611fbe4564SMatthias Ringwald
1621fbe4564SMatthias Ringwald    def packet_handler(self, packet_type, packet):
1631fbe4564SMatthias Ringwald        opcode = little_endian_read_16(packet, 0)
1641fbe4564SMatthias Ringwald        # print ("%s, opcode 0x%04x" % (self.name, opcode))
1651fbe4564SMatthias Ringwald        if opcode == 0x0c03:
1661fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
1671fbe4564SMatthias Ringwald            return
1681fbe4564SMatthias Ringwald        if opcode == 0x1001:
1691fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x10\x00\x06\x86\x1d\x06\x0a\x00\x86\x1d')
1701fbe4564SMatthias Ringwald            return
1711fbe4564SMatthias Ringwald        if opcode == 0x0c14:
1721fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.name)
1731fbe4564SMatthias Ringwald            return
1741fbe4564SMatthias Ringwald        if opcode == 0x1002:
1751fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\xff\xff\xff\x03\xfe\xff\xff\xff\xff\xff\xff\xff\xf3\x0f\xe8\xfe\x3f\xf7\x83\xff\x1c\x00\x00\x00\x61\xf7\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
1761fbe4564SMatthias Ringwald            return
1771fbe4564SMatthias Ringwald        if opcode == 0x1009:
1781fbe4564SMatthias Ringwald            # read bd_addr
1791fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.bd_addr[::-1])
1801fbe4564SMatthias Ringwald            return
1811fbe4564SMatthias Ringwald        if opcode == 0x1005:
1821fbe4564SMatthias Ringwald            # read buffer size
1831fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x36\x01\x40\x0a\x00\x08\x00')
1841fbe4564SMatthias Ringwald            return
1851fbe4564SMatthias Ringwald        if opcode == 0x1003:
1861fbe4564SMatthias Ringwald            # read local supported features
1871fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\xff\xff\x8f\xfe\xf8\xff\x5b\x87')
1881fbe4564SMatthias Ringwald            return
1891fbe4564SMatthias Ringwald        if opcode == 0x0c01:
1901fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
1911fbe4564SMatthias Ringwald            return
1921fbe4564SMatthias Ringwald        if opcode == 0x2002:
1931fbe4564SMatthias Ringwald            # le read buffer size
1941fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x00\x00\x00')
1951fbe4564SMatthias Ringwald            return
1961fbe4564SMatthias Ringwald        if opcode == 0x200f:
1971fbe4564SMatthias Ringwald            # read whitelist size
1981fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x19')
1991fbe4564SMatthias Ringwald            return
2001fbe4564SMatthias Ringwald        if opcode == 0x200b:
2011fbe4564SMatthias Ringwald            # set scan parameters
2021fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2031fbe4564SMatthias Ringwald            return
2041fbe4564SMatthias Ringwald        if opcode == 0x200c:
2051fbe4564SMatthias Ringwald            # set scan enabled
2061fbe4564SMatthias Ringwald            self.scan_enabled = ord(packet[3])
2071fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2081fbe4564SMatthias Ringwald            return
2091fbe4564SMatthias Ringwald        if opcode == 0x0c6d:
2101fbe4564SMatthias Ringwald            # write le host supported
2111fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2121fbe4564SMatthias Ringwald            return
2131fbe4564SMatthias Ringwald        if opcode == 0x2017:
2141fbe4564SMatthias Ringwald            # LE Encrypt - key 16, data 16
2151fbe4564SMatthias Ringwald            key = packet[18:2:-1]
2161fbe4564SMatthias Ringwald            data = packet[35:18:-1]
2171fbe4564SMatthias Ringwald            cipher = AES.new(key)
2181fbe4564SMatthias Ringwald            result = cipher.encrypt(data)
2191fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, result[::-1])
2201fbe4564SMatthias Ringwald            return
2211fbe4564SMatthias Ringwald        if opcode == 0x2018:
2221fbe4564SMatthias Ringwald            # LE Rand
2231fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.random.read(8))
2241fbe4564SMatthias Ringwald            return
2251fbe4564SMatthias Ringwald        if opcode == 0x2006:
2261fbe4564SMatthias Ringwald            # Set Adv Params
2271fbe4564SMatthias Ringwald            self.handle_set_adv_params(little_endian_read_16(packet,3), little_endian_read_16(packet,5), ord(packet[6]))
2281fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2291fbe4564SMatthias Ringwald            return
2301fbe4564SMatthias Ringwald        if opcode == 0x2008:
2311fbe4564SMatthias Ringwald            # Set Adv Data
2321fbe4564SMatthias Ringwald            len = ord(packet[3])
2331fbe4564SMatthias Ringwald            self.handle_set_adv_data(packet[4:4+len])
2341fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2351fbe4564SMatthias Ringwald            return
2361fbe4564SMatthias Ringwald        if opcode == 0x200a:
2371fbe4564SMatthias Ringwald            # Set Adv Enable
2381fbe4564SMatthias Ringwald            self.handle_set_adv_enable(ord(packet[3]))
2391fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
2401fbe4564SMatthias Ringwald            return
2411fbe4564SMatthias Ringwald        print("Opcode 0x%0x not handled!" % opcode)
2421fbe4564SMatthias Ringwald
2431fbe4564SMatthias Ringwaldclass Node:
2441fbe4564SMatthias Ringwald
2451fbe4564SMatthias Ringwald    def __init__(self):
2461fbe4564SMatthias Ringwald        self.name = 'node'
2471fbe4564SMatthias Ringwald        self.master = -1
2481fbe4564SMatthias Ringwald        self.slave  = -1
2491fbe4564SMatthias Ringwald        self.slave_ttyname = ''
2501fbe4564SMatthias Ringwald        self.controller = HCIController()
2511fbe4564SMatthias Ringwald
2521fbe4564SMatthias Ringwald    def set_name(self, name):
2531fbe4564SMatthias Ringwald        self.controller.set_name(name)
2541fbe4564SMatthias Ringwald        self.name = name
2551fbe4564SMatthias Ringwald
2561fbe4564SMatthias Ringwald    def get_name(self):
2571fbe4564SMatthias Ringwald        return self.name
2581fbe4564SMatthias Ringwald
2591fbe4564SMatthias Ringwald    def set_bd_addr(self, bd_addr):
2601fbe4564SMatthias Ringwald        self.controller.set_bd_addr(bd_addr)
2611fbe4564SMatthias Ringwald
2621fbe4564SMatthias Ringwald    def start_process(self):
2631fbe4564SMatthias Ringwald        print('Node: %s' % self.name)
2641fbe4564SMatthias Ringwald        (self.master, self.slave) = pty.openpty()
2651fbe4564SMatthias Ringwald        self.slave_ttyname = os.ttyname(self.slave)
2661fbe4564SMatthias Ringwald        print('- tty %s' % self.slave_ttyname)
2671fbe4564SMatthias Ringwald        print('- fd %u' % self.master)
2681fbe4564SMatthias Ringwald        self.controller.set_fd(self.master)
2691fbe4564SMatthias Ringwald        subprocess.Popen(['./mesh', '-d', self.slave_ttyname])
2701fbe4564SMatthias Ringwald
2711fbe4564SMatthias Ringwald    def get_master(self):
2721fbe4564SMatthias Ringwald        return self.master
2731fbe4564SMatthias Ringwald
2741fbe4564SMatthias Ringwald    def parse(self, c):
2751fbe4564SMatthias Ringwald        self.controller.parse(c)
2761fbe4564SMatthias Ringwald
2771fbe4564SMatthias Ringwald    def set_adv_handler(self, adv_handler, adv_handler_context):
2781fbe4564SMatthias Ringwald        self.controller.set_adv_handler(adv_handler, adv_handler_context)
2791fbe4564SMatthias Ringwald
2801fbe4564SMatthias Ringwald    def inject_packet(self, event):
2811fbe4564SMatthias Ringwald        os.write(self.master, event)
2821fbe4564SMatthias Ringwald
2831fbe4564SMatthias Ringwald    def is_scanning(self):
2841fbe4564SMatthias Ringwald        return self.controller.is_scanning()
2851fbe4564SMatthias Ringwald
2861fbe4564SMatthias Ringwalddef get_time_millis():
2871fbe4564SMatthias Ringwald    return int(round(time.time() * 1000))
2881fbe4564SMatthias Ringwald
2891fbe4564SMatthias Ringwalddef add_timer(timeout_ms, callback, context):
2901fbe4564SMatthias Ringwald    global timers_timeouts
2911fbe4564SMatthias Ringwald    global timers_callbacks
2921fbe4564SMatthias Ringwald
2931fbe4564SMatthias Ringwald    timeout = get_time_millis() + timeout_ms;
2941fbe4564SMatthias Ringwald    pos = bisect.bisect(timers_timeouts, timeout)
2951fbe4564SMatthias Ringwald    timers_timeouts.insert(pos, timeout)
2961fbe4564SMatthias Ringwald    timers_callbacks.insert(pos, (callback, context))
2971fbe4564SMatthias Ringwald
2981fbe4564SMatthias Ringwalddef remove_timer(callback, context):
2991fbe4564SMatthias Ringwald    if (callback, context) in timers_callbacks:
3001fbe4564SMatthias Ringwald        indices = [timers_callbacks.index(t) for t in timers_callbacks if t[0] == callback and t[1] == context]
3011fbe4564SMatthias Ringwald        index = indices[0]
3021fbe4564SMatthias Ringwald        timers_callbacks.pop(index)
3031fbe4564SMatthias Ringwald        timers_timeouts.pop(index)
3041fbe4564SMatthias Ringwald
3051fbe4564SMatthias Ringwalddef run(nodes):
3061fbe4564SMatthias Ringwald    # create map fd -> node
3071fbe4564SMatthias Ringwald    nodes_by_fd = { node.get_master():node for node in nodes}
3081fbe4564SMatthias Ringwald    read_fds = nodes_by_fd.keys()
3091fbe4564SMatthias Ringwald    while True:
3101fbe4564SMatthias Ringwald        # process expired timers
3111fbe4564SMatthias Ringwald        time_ms = get_time_millis()
3121fbe4564SMatthias Ringwald        while len(timers_timeouts) and timers_timeouts[0] < time_ms:
3131fbe4564SMatthias Ringwald            timers_timeouts.pop(0)
3141fbe4564SMatthias Ringwald            (callback,context) = timers_callbacks.pop(0)
3151fbe4564SMatthias Ringwald            callback(context)
3161fbe4564SMatthias Ringwald        # timer timers_timeouts?
3171fbe4564SMatthias Ringwald        if len(timers_timeouts):
3181fbe4564SMatthias Ringwald            timeout = (timers_timeouts[0] - time_ms) / 1000.0
3191fbe4564SMatthias Ringwald            (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[], timeout)
3201fbe4564SMatthias Ringwald        else:
3211fbe4564SMatthias Ringwald            (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[])
3221fbe4564SMatthias Ringwald        for fd in read_ready:
3231fbe4564SMatthias Ringwald            node = nodes_by_fd[fd]
3241fbe4564SMatthias Ringwald            c = os.read(fd, 1)
3251fbe4564SMatthias Ringwald            node.parse(c)
3261fbe4564SMatthias Ringwald
3271fbe4564SMatthias Ringwalddef adv_handler(src_node, event):
3281fbe4564SMatthias Ringwald    global nodes
3291fbe4564SMatthias Ringwald    # print('adv from %s' % src_node.get_name())
3301fbe4564SMatthias Ringwald    for dst_node in nodes:
3311fbe4564SMatthias Ringwald        if src_node == dst_node:
3321fbe4564SMatthias Ringwald            continue
3331fbe4564SMatthias Ringwald        if not dst_node.is_scanning():
3341fbe4564SMatthias Ringwald            continue
3351fbe4564SMatthias Ringwald        print('Adv %s -> %s - %s' % (src_node.get_name(), dst_node.get_name(), as_hex(event[14:-1])))
3361fbe4564SMatthias Ringwald        dst_node.inject_packet(event)
3371fbe4564SMatthias Ringwald
3381fbe4564SMatthias Ringwald# parse configuration file passed in via cmd line args
3391fbe4564SMatthias Ringwald# TODO
3401fbe4564SMatthias Ringwald
3411fbe4564SMatthias Ringwaldnode1 = Node()
3421fbe4564SMatthias Ringwaldnode1.set_name('node_1')
3431fbe4564SMatthias Ringwaldnode1.set_bd_addr('aaaaaa')
3441fbe4564SMatthias Ringwaldnode1.set_adv_handler(adv_handler, node1)
3451fbe4564SMatthias Ringwaldnode1.start_process()
3461fbe4564SMatthias Ringwald
3471fbe4564SMatthias Ringwaldnode2 = Node()
3481fbe4564SMatthias Ringwaldnode2.set_name('node_2')
3491fbe4564SMatthias Ringwaldnode2.set_bd_addr('bbbbbb')
3501fbe4564SMatthias Ringwaldnode2.set_adv_handler(adv_handler, node2)
3511fbe4564SMatthias Ringwaldnode2.start_process()
3521fbe4564SMatthias Ringwald
3531fbe4564SMatthias Ringwaldnodes = [node1, node2]
3541fbe4564SMatthias Ringwald
3551fbe4564SMatthias Ringwaldrun(nodes)
356