xref: /btstack/test/mesh/simulator.py (revision 1fbe4564be530ba975f4cd70443a76f0afda5276)
1*1fbe4564SMatthias Ringwald#!/usr/bin/env python
2*1fbe4564SMatthias Ringwald#
3*1fbe4564SMatthias Ringwald# Simulate network of Bluetooth Controllers
4*1fbe4564SMatthias Ringwald#
5*1fbe4564SMatthias Ringwald# Each simulated controller has an HCI H4 interface
6*1fbe4564SMatthias Ringwald# Network configuration will be stored in a YAML file or similar
7*1fbe4564SMatthias Ringwald#
8*1fbe4564SMatthias Ringwald# Copyright 2017 BlueKitchen GmbH
9*1fbe4564SMatthias Ringwald#
10*1fbe4564SMatthias Ringwald
11*1fbe4564SMatthias Ringwald
12*1fbe4564SMatthias Ringwaldimport os
13*1fbe4564SMatthias Ringwaldimport pty
14*1fbe4564SMatthias Ringwaldimport select
15*1fbe4564SMatthias Ringwaldimport subprocess
16*1fbe4564SMatthias Ringwaldimport sys
17*1fbe4564SMatthias Ringwaldimport bisect
18*1fbe4564SMatthias Ringwaldimport time
19*1fbe4564SMatthias Ringwald
20*1fbe4564SMatthias Ringwaldfrom Crypto.Cipher import AES
21*1fbe4564SMatthias Ringwaldfrom Crypto import Random
22*1fbe4564SMatthias Ringwald
23*1fbe4564SMatthias Ringwalddef little_endian_read_16(buffer, pos):
24*1fbe4564SMatthias Ringwald    return ord(buffer[pos]) + (ord(buffer[pos+1]) << 8)
25*1fbe4564SMatthias Ringwald
26*1fbe4564SMatthias Ringwalddef as_hex(data):
27*1fbe4564SMatthias Ringwald    str_list = []
28*1fbe4564SMatthias Ringwald    for byte in data:
29*1fbe4564SMatthias Ringwald        str_list.append("{0:02x} ".format(ord(byte)))
30*1fbe4564SMatthias Ringwald    return ''.join(str_list)
31*1fbe4564SMatthias Ringwald
32*1fbe4564SMatthias Ringwaldadv_type_names = ['ADV_IND', 'ADV_DIRECT_IND_HIGH', 'ADV_SCAN_IND', 'ADV_NONCONN_IND', 'ADV_DIRECT_IND_LOW']
33*1fbe4564SMatthias Ringwaldtimers_timeouts = []
34*1fbe4564SMatthias Ringwaldtimers_callbacks = []
35*1fbe4564SMatthias Ringwald
36*1fbe4564SMatthias Ringwaldclass H4Parser:
37*1fbe4564SMatthias Ringwald
38*1fbe4564SMatthias Ringwald    def __init__(self):
39*1fbe4564SMatthias Ringwald        self.packet_type = "NONE"
40*1fbe4564SMatthias Ringwald        self.reset()
41*1fbe4564SMatthias Ringwald
42*1fbe4564SMatthias Ringwald    def set_packet_handler(self, handler):
43*1fbe4564SMatthias Ringwald        self.handler = handler
44*1fbe4564SMatthias Ringwald
45*1fbe4564SMatthias Ringwald    def reset(self):
46*1fbe4564SMatthias Ringwald        self.bytes_to_read = 1
47*1fbe4564SMatthias Ringwald        self.buffer = ''
48*1fbe4564SMatthias Ringwald        self.state = "H4_W4_PACKET_TYPE"
49*1fbe4564SMatthias Ringwald
50*1fbe4564SMatthias Ringwald    def parse(self, data):
51*1fbe4564SMatthias Ringwald        self.buffer += data
52*1fbe4564SMatthias Ringwald        self.bytes_to_read -= 1
53*1fbe4564SMatthias Ringwald        if self.bytes_to_read == 0:
54*1fbe4564SMatthias Ringwald            if self.state == "H4_W4_PACKET_TYPE":
55*1fbe4564SMatthias Ringwald                self.buffer = ''
56*1fbe4564SMatthias Ringwald                if data == chr(1):
57*1fbe4564SMatthias Ringwald                    # cmd
58*1fbe4564SMatthias Ringwald                    self.packet_type = "CMD"
59*1fbe4564SMatthias Ringwald                    self.state = "W4_CMD_HEADER"
60*1fbe4564SMatthias Ringwald                    self.bytes_to_read = 3
61*1fbe4564SMatthias Ringwald                if data == chr(2):
62*1fbe4564SMatthias Ringwald                    # acl
63*1fbe4564SMatthias Ringwald                    self.packet_type = "ACL"
64*1fbe4564SMatthias Ringwald                    self.state = "W4_ACL_HEADER"
65*1fbe4564SMatthias Ringwald                    self.bytes_to_read = 4
66*1fbe4564SMatthias Ringwald                return
67*1fbe4564SMatthias Ringwald            if self.state == "W4_CMD_HEADER":
68*1fbe4564SMatthias Ringwald                self.bytes_to_read = ord(self.buffer[2])
69*1fbe4564SMatthias Ringwald                self.state = "H4_W4_PAYLOAD"
70*1fbe4564SMatthias Ringwald                if self.bytes_to_read > 0:
71*1fbe4564SMatthias Ringwald                    return
72*1fbe4564SMatthias Ringwald                # fall through to handle payload len = 0
73*1fbe4564SMatthias Ringwald            if self.state == "W4_ACL_HEADER":
74*1fbe4564SMatthias Ringwald                self.bytes_to_read = little_endian_read_16(buffer, 2)
75*1fbe4564SMatthias Ringwald                self.state = "H4_W4_PAYLOAD"
76*1fbe4564SMatthias Ringwald                if self.bytes_to_read > 0:
77*1fbe4564SMatthias Ringwald                    return
78*1fbe4564SMatthias Ringwald                # fall through to handle payload len = 0
79*1fbe4564SMatthias Ringwald            if self.state == "H4_W4_PAYLOAD":
80*1fbe4564SMatthias Ringwald                self.handler(self.packet_type, self.buffer)
81*1fbe4564SMatthias Ringwald                self.reset()
82*1fbe4564SMatthias Ringwald                return
83*1fbe4564SMatthias Ringwald
84*1fbe4564SMatthias Ringwaldclass HCIController:
85*1fbe4564SMatthias Ringwald
86*1fbe4564SMatthias Ringwald    def __init__(self):
87*1fbe4564SMatthias Ringwald        self.fd = -1
88*1fbe4564SMatthias Ringwald        self.random = Random.new()
89*1fbe4564SMatthias Ringwald        self.name = 'BTstack Mesh Simulator'
90*1fbe4564SMatthias Ringwald        self.bd_addr = 'aaaaaa'
91*1fbe4564SMatthias Ringwald        self.parser = H4Parser()
92*1fbe4564SMatthias Ringwald        self.parser.set_packet_handler(self.packet_handler)
93*1fbe4564SMatthias Ringwald        self.adv_enabled = 0
94*1fbe4564SMatthias Ringwald        self.adv_type = 0
95*1fbe4564SMatthias Ringwald        self.adv_interval_min = 0
96*1fbe4564SMatthias Ringwald        self.adv_interval_max = 0
97*1fbe4564SMatthias Ringwald        self.adv_data = ''
98*1fbe4564SMatthias Ringwald        self.scan_enabled = False
99*1fbe4564SMatthias Ringwald
100*1fbe4564SMatthias Ringwald    def parse(self, data):
101*1fbe4564SMatthias Ringwald        self.parser.parse(data)
102*1fbe4564SMatthias Ringwald
103*1fbe4564SMatthias Ringwald    def set_fd(self,fd):
104*1fbe4564SMatthias Ringwald        self.fd = fd
105*1fbe4564SMatthias Ringwald
106*1fbe4564SMatthias Ringwald    def set_bd_addr(self, bd_addr):
107*1fbe4564SMatthias Ringwald        self.bd_addr = bd_addr
108*1fbe4564SMatthias Ringwald
109*1fbe4564SMatthias Ringwald    def set_name(self, name):
110*1fbe4564SMatthias Ringwald        self.name = name
111*1fbe4564SMatthias Ringwald
112*1fbe4564SMatthias Ringwald    def set_adv_handler(self, adv_handler, adv_handler_context):
113*1fbe4564SMatthias Ringwald        self.adv_handler         = adv_handler
114*1fbe4564SMatthias Ringwald        self.adv_handler_context = adv_handler_context
115*1fbe4564SMatthias Ringwald
116*1fbe4564SMatthias Ringwald    def is_scanning(self):
117*1fbe4564SMatthias Ringwald        return self.scan_enabled
118*1fbe4564SMatthias Ringwald
119*1fbe4564SMatthias Ringwald    def emit_command_complete(self, opcode, result):
120*1fbe4564SMatthias Ringwald        # type, event, len, num commands, opcode, result
121*1fbe4564SMatthias Ringwald        os.write(self.fd, '\x04\x0e' + chr(3 + len(result)) + chr(1) + chr(opcode & 255)  + chr(opcode >> 8) + result)
122*1fbe4564SMatthias Ringwald
123*1fbe4564SMatthias Ringwald    def emit_adv_report(self, event_type, rssi):
124*1fbe4564SMatthias Ringwald        # type, event, len, Subevent_Code, Num_Reports, Event_Type[i], Address_Type[i], Address[i], Length[i], Data[i], RSSI[i]
125*1fbe4564SMatthias 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)
126*1fbe4564SMatthias Ringwald        self.adv_handler(self.adv_handler_context, event)
127*1fbe4564SMatthias Ringwald
128*1fbe4564SMatthias Ringwald    def handle_set_adv_enable(self, enable):
129*1fbe4564SMatthias Ringwald        self.adv_enabled = enable
130*1fbe4564SMatthias Ringwald        print('Node %s adv enable %u' % (self.name, self.adv_enabled))
131*1fbe4564SMatthias Ringwald        if self.adv_enabled:
132*1fbe4564SMatthias Ringwald            add_timer(1, self.handle_adv_timer, self)
133*1fbe4564SMatthias Ringwald        else:
134*1fbe4564SMatthias Ringwald            remove_timer(self.handle_adv_timer, self)
135*1fbe4564SMatthias Ringwald
136*1fbe4564SMatthias Ringwald    def handle_set_adv_data(self, data):
137*1fbe4564SMatthias Ringwald        self.adv_data = data
138*1fbe4564SMatthias Ringwald        print('Node %s adv data %s' % (self.name, as_hex(self.adv_data)))
139*1fbe4564SMatthias Ringwald
140*1fbe4564SMatthias Ringwald    def handle_set_adv_params(self, interval_min, interval_max, adv_type):
141*1fbe4564SMatthias Ringwald        self.adv_interval_min = interval_min * 0.625
142*1fbe4564SMatthias Ringwald        self.adv_interval_max = interval_max * 0.625
143*1fbe4564SMatthias Ringwald        self.adv_type         = adv_type
144*1fbe4564SMatthias 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]))
145*1fbe4564SMatthias Ringwald
146*1fbe4564SMatthias Ringwald    def handle_adv_timer(self, context):
147*1fbe4564SMatthias Ringwald        if self.adv_enabled:
148*1fbe4564SMatthias Ringwald            self.emit_adv_report(0, 0)
149*1fbe4564SMatthias Ringwald            add_timer(self.adv_interval_min, self.handle_adv_timer, self)
150*1fbe4564SMatthias Ringwald
151*1fbe4564SMatthias Ringwald    def packet_handler(self, packet_type, packet):
152*1fbe4564SMatthias Ringwald        opcode = little_endian_read_16(packet, 0)
153*1fbe4564SMatthias Ringwald        # print ("%s, opcode 0x%04x" % (self.name, opcode))
154*1fbe4564SMatthias Ringwald        if opcode == 0x0c03:
155*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
156*1fbe4564SMatthias Ringwald            return
157*1fbe4564SMatthias Ringwald        if opcode == 0x1001:
158*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x10\x00\x06\x86\x1d\x06\x0a\x00\x86\x1d')
159*1fbe4564SMatthias Ringwald            return
160*1fbe4564SMatthias Ringwald        if opcode == 0x0c14:
161*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.name)
162*1fbe4564SMatthias Ringwald            return
163*1fbe4564SMatthias Ringwald        if opcode == 0x1002:
164*1fbe4564SMatthias 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')
165*1fbe4564SMatthias Ringwald            return
166*1fbe4564SMatthias Ringwald        if opcode == 0x1009:
167*1fbe4564SMatthias Ringwald            # read bd_addr
168*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.bd_addr[::-1])
169*1fbe4564SMatthias Ringwald            return
170*1fbe4564SMatthias Ringwald        if opcode == 0x1005:
171*1fbe4564SMatthias Ringwald            # read buffer size
172*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x36\x01\x40\x0a\x00\x08\x00')
173*1fbe4564SMatthias Ringwald            return
174*1fbe4564SMatthias Ringwald        if opcode == 0x1003:
175*1fbe4564SMatthias Ringwald            # read local supported features
176*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\xff\xff\x8f\xfe\xf8\xff\x5b\x87')
177*1fbe4564SMatthias Ringwald            return
178*1fbe4564SMatthias Ringwald        if opcode == 0x0c01:
179*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
180*1fbe4564SMatthias Ringwald            return
181*1fbe4564SMatthias Ringwald        if opcode == 0x2002:
182*1fbe4564SMatthias Ringwald            # le read buffer size
183*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x00\x00\x00')
184*1fbe4564SMatthias Ringwald            return
185*1fbe4564SMatthias Ringwald        if opcode == 0x200f:
186*1fbe4564SMatthias Ringwald            # read whitelist size
187*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00\x19')
188*1fbe4564SMatthias Ringwald            return
189*1fbe4564SMatthias Ringwald        if opcode == 0x200b:
190*1fbe4564SMatthias Ringwald            # set scan parameters
191*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
192*1fbe4564SMatthias Ringwald            return
193*1fbe4564SMatthias Ringwald        if opcode == 0x200c:
194*1fbe4564SMatthias Ringwald            # set scan enabled
195*1fbe4564SMatthias Ringwald            self.scan_enabled = ord(packet[3])
196*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
197*1fbe4564SMatthias Ringwald            return
198*1fbe4564SMatthias Ringwald        if opcode == 0x0c6d:
199*1fbe4564SMatthias Ringwald            # write le host supported
200*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
201*1fbe4564SMatthias Ringwald            return
202*1fbe4564SMatthias Ringwald        if opcode == 0x2017:
203*1fbe4564SMatthias Ringwald            # LE Encrypt - key 16, data 16
204*1fbe4564SMatthias Ringwald            key = packet[18:2:-1]
205*1fbe4564SMatthias Ringwald            data = packet[35:18:-1]
206*1fbe4564SMatthias Ringwald            cipher = AES.new(key)
207*1fbe4564SMatthias Ringwald            result = cipher.encrypt(data)
208*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, result[::-1])
209*1fbe4564SMatthias Ringwald            return
210*1fbe4564SMatthias Ringwald        if opcode == 0x2018:
211*1fbe4564SMatthias Ringwald            # LE Rand
212*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00' + self.random.read(8))
213*1fbe4564SMatthias Ringwald            return
214*1fbe4564SMatthias Ringwald        if opcode == 0x2006:
215*1fbe4564SMatthias Ringwald            # Set Adv Params
216*1fbe4564SMatthias Ringwald            self.handle_set_adv_params(little_endian_read_16(packet,3), little_endian_read_16(packet,5), ord(packet[6]))
217*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
218*1fbe4564SMatthias Ringwald            return
219*1fbe4564SMatthias Ringwald        if opcode == 0x2008:
220*1fbe4564SMatthias Ringwald            # Set Adv Data
221*1fbe4564SMatthias Ringwald            len = ord(packet[3])
222*1fbe4564SMatthias Ringwald            self.handle_set_adv_data(packet[4:4+len])
223*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
224*1fbe4564SMatthias Ringwald            return
225*1fbe4564SMatthias Ringwald        if opcode == 0x200a:
226*1fbe4564SMatthias Ringwald            # Set Adv Enable
227*1fbe4564SMatthias Ringwald            self.handle_set_adv_enable(ord(packet[3]))
228*1fbe4564SMatthias Ringwald            self.emit_command_complete(opcode, '\x00')
229*1fbe4564SMatthias Ringwald            return
230*1fbe4564SMatthias Ringwald        print("Opcode 0x%0x not handled!" % opcode)
231*1fbe4564SMatthias Ringwald
232*1fbe4564SMatthias Ringwaldclass Node:
233*1fbe4564SMatthias Ringwald
234*1fbe4564SMatthias Ringwald    def __init__(self):
235*1fbe4564SMatthias Ringwald        self.name = 'node'
236*1fbe4564SMatthias Ringwald        self.master = -1
237*1fbe4564SMatthias Ringwald        self.slave  = -1
238*1fbe4564SMatthias Ringwald        self.slave_ttyname = ''
239*1fbe4564SMatthias Ringwald        self.controller = HCIController()
240*1fbe4564SMatthias Ringwald
241*1fbe4564SMatthias Ringwald    def set_name(self, name):
242*1fbe4564SMatthias Ringwald        self.controller.set_name(name)
243*1fbe4564SMatthias Ringwald        self.name = name
244*1fbe4564SMatthias Ringwald
245*1fbe4564SMatthias Ringwald    def get_name(self):
246*1fbe4564SMatthias Ringwald        return self.name
247*1fbe4564SMatthias Ringwald
248*1fbe4564SMatthias Ringwald    def set_bd_addr(self, bd_addr):
249*1fbe4564SMatthias Ringwald        self.controller.set_bd_addr(bd_addr)
250*1fbe4564SMatthias Ringwald
251*1fbe4564SMatthias Ringwald    def start_process(self):
252*1fbe4564SMatthias Ringwald        print('Node: %s' % self.name)
253*1fbe4564SMatthias Ringwald        (self.master, self.slave) = pty.openpty()
254*1fbe4564SMatthias Ringwald        self.slave_ttyname = os.ttyname(self.slave)
255*1fbe4564SMatthias Ringwald        print('- tty %s' % self.slave_ttyname)
256*1fbe4564SMatthias Ringwald        print('- fd %u' % self.master)
257*1fbe4564SMatthias Ringwald        self.controller.set_fd(self.master)
258*1fbe4564SMatthias Ringwald        subprocess.Popen(['./mesh', '-d', self.slave_ttyname])
259*1fbe4564SMatthias Ringwald
260*1fbe4564SMatthias Ringwald    def get_master(self):
261*1fbe4564SMatthias Ringwald        return self.master
262*1fbe4564SMatthias Ringwald
263*1fbe4564SMatthias Ringwald    def parse(self, c):
264*1fbe4564SMatthias Ringwald        self.controller.parse(c)
265*1fbe4564SMatthias Ringwald
266*1fbe4564SMatthias Ringwald    def set_adv_handler(self, adv_handler, adv_handler_context):
267*1fbe4564SMatthias Ringwald        self.controller.set_adv_handler(adv_handler, adv_handler_context)
268*1fbe4564SMatthias Ringwald
269*1fbe4564SMatthias Ringwald    def inject_packet(self, event):
270*1fbe4564SMatthias Ringwald        os.write(self.master, event)
271*1fbe4564SMatthias Ringwald
272*1fbe4564SMatthias Ringwald    def is_scanning(self):
273*1fbe4564SMatthias Ringwald        return self.controller.is_scanning()
274*1fbe4564SMatthias Ringwald
275*1fbe4564SMatthias Ringwalddef get_time_millis():
276*1fbe4564SMatthias Ringwald    return int(round(time.time() * 1000))
277*1fbe4564SMatthias Ringwald
278*1fbe4564SMatthias Ringwalddef add_timer(timeout_ms, callback, context):
279*1fbe4564SMatthias Ringwald    global timers_timeouts
280*1fbe4564SMatthias Ringwald    global timers_callbacks
281*1fbe4564SMatthias Ringwald
282*1fbe4564SMatthias Ringwald    timeout = get_time_millis() + timeout_ms;
283*1fbe4564SMatthias Ringwald    pos = bisect.bisect(timers_timeouts, timeout)
284*1fbe4564SMatthias Ringwald    timers_timeouts.insert(pos, timeout)
285*1fbe4564SMatthias Ringwald    timers_callbacks.insert(pos, (callback, context))
286*1fbe4564SMatthias Ringwald
287*1fbe4564SMatthias Ringwalddef remove_timer(callback, context):
288*1fbe4564SMatthias Ringwald    if (callback, context) in timers_callbacks:
289*1fbe4564SMatthias Ringwald        indices = [timers_callbacks.index(t) for t in timers_callbacks if t[0] == callback and t[1] == context]
290*1fbe4564SMatthias Ringwald        index = indices[0]
291*1fbe4564SMatthias Ringwald        timers_callbacks.pop(index)
292*1fbe4564SMatthias Ringwald        timers_timeouts.pop(index)
293*1fbe4564SMatthias Ringwald
294*1fbe4564SMatthias Ringwalddef run(nodes):
295*1fbe4564SMatthias Ringwald    # create map fd -> node
296*1fbe4564SMatthias Ringwald    nodes_by_fd = { node.get_master():node for node in nodes}
297*1fbe4564SMatthias Ringwald    read_fds = nodes_by_fd.keys()
298*1fbe4564SMatthias Ringwald    while True:
299*1fbe4564SMatthias Ringwald        # process expired timers
300*1fbe4564SMatthias Ringwald        time_ms = get_time_millis()
301*1fbe4564SMatthias Ringwald        while len(timers_timeouts) and timers_timeouts[0] < time_ms:
302*1fbe4564SMatthias Ringwald            timers_timeouts.pop(0)
303*1fbe4564SMatthias Ringwald            (callback,context) = timers_callbacks.pop(0)
304*1fbe4564SMatthias Ringwald            callback(context)
305*1fbe4564SMatthias Ringwald        # timer timers_timeouts?
306*1fbe4564SMatthias Ringwald        if len(timers_timeouts):
307*1fbe4564SMatthias Ringwald            timeout = (timers_timeouts[0] - time_ms) / 1000.0
308*1fbe4564SMatthias Ringwald            (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[], timeout)
309*1fbe4564SMatthias Ringwald        else:
310*1fbe4564SMatthias Ringwald            (read_ready, write_ready, exception_ready) = select.select(read_fds,[],[])
311*1fbe4564SMatthias Ringwald        for fd in read_ready:
312*1fbe4564SMatthias Ringwald            node = nodes_by_fd[fd]
313*1fbe4564SMatthias Ringwald            c = os.read(fd, 1)
314*1fbe4564SMatthias Ringwald            node.parse(c)
315*1fbe4564SMatthias Ringwald
316*1fbe4564SMatthias Ringwalddef adv_handler(src_node, event):
317*1fbe4564SMatthias Ringwald    global nodes
318*1fbe4564SMatthias Ringwald    # print('adv from %s' % src_node.get_name())
319*1fbe4564SMatthias Ringwald    for dst_node in nodes:
320*1fbe4564SMatthias Ringwald        if src_node == dst_node:
321*1fbe4564SMatthias Ringwald            continue
322*1fbe4564SMatthias Ringwald        if not dst_node.is_scanning():
323*1fbe4564SMatthias Ringwald            continue
324*1fbe4564SMatthias Ringwald        print('Adv %s -> %s - %s' % (src_node.get_name(), dst_node.get_name(), as_hex(event[14:-1])))
325*1fbe4564SMatthias Ringwald        dst_node.inject_packet(event)
326*1fbe4564SMatthias Ringwald
327*1fbe4564SMatthias Ringwald# parse configuration file passed in via cmd line args
328*1fbe4564SMatthias Ringwald# TODO
329*1fbe4564SMatthias Ringwald
330*1fbe4564SMatthias Ringwaldnode1 = Node()
331*1fbe4564SMatthias Ringwaldnode1.set_name('node_1')
332*1fbe4564SMatthias Ringwaldnode1.set_bd_addr('aaaaaa')
333*1fbe4564SMatthias Ringwaldnode1.set_adv_handler(adv_handler, node1)
334*1fbe4564SMatthias Ringwaldnode1.start_process()
335*1fbe4564SMatthias Ringwald
336*1fbe4564SMatthias Ringwaldnode2 = Node()
337*1fbe4564SMatthias Ringwaldnode2.set_name('node_2')
338*1fbe4564SMatthias Ringwaldnode2.set_bd_addr('bbbbbb')
339*1fbe4564SMatthias Ringwaldnode2.set_adv_handler(adv_handler, node2)
340*1fbe4564SMatthias Ringwaldnode2.start_process()
341*1fbe4564SMatthias Ringwald
342*1fbe4564SMatthias Ringwaldnodes = [node1, node2]
343*1fbe4564SMatthias Ringwald
344*1fbe4564SMatthias Ringwaldrun(nodes)
345