1#!/usr/bin/env python3 2# BlueKitchen GmbH (c) 2014 3 4import re 5import os 6import sys 7 8# paths 9bluetooth_h_path = 'src/bluetooth.h' 10btstack_defines_h_path = 'src/btstack_defines.h' 11daemon_cmds_c_path = 'platform/daemon/src/daemon_cmds.c' 12daemon_cmds_h_path = 'platform/daemon/src/daemon_cmds.h' 13hci_cmds_c_path = 'src/hci_cmd.c' 14hci_cmds_h_path = 'src/hci_cmd.h' 15hci_h_path = 'src/hci.h' 16 17open_bracket = '[' 18closing_bracket = ']' 19 20btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 21print ("BTstack root %s" % btstack_root) 22 23def set_btstack_root(path): 24 global btstack_root 25 btstack_root = path 26 27def assert_dir(path): 28 if not os.access(path, os.R_OK): 29 os.makedirs(path) 30 31def cap(x): 32 if x.lower() == 'btstack': 33 return 'BTstack' 34 acronyms = ['ATT', 'GAP', 'GATT', 'HCI', 'L2CAP', 'LE', 'RFCOMM', 'SM', 'SDP', 'UUID16', 'UUID128', 'HSP', 'HFP', 'ANCS'] 35 if x.upper() in acronyms: 36 return x.upper() 37 return x.capitalize() 38 39def camel_case(name): 40 return ''.join(map(cap, name.split('_'))) 41 42def camel_case_var(name): 43 if name in ['uuid128', 'uuid16']: 44 return name 45 camel = camel_case(name) 46 return camel[0].lower() + camel[1:] 47 48def read_defines(infile): 49 defines = dict() 50 with open (infile, 'rt') as fin: 51 for line in fin: 52 parts = re.match(r'#define\s+(\w+)\s+(\w*)[u]',line) 53 if parts and len(parts.groups()) == 2: 54 (key, value) = parts.groups() 55 defines[key] = value.lower() 56 return defines 57 58def parse_defines(): 59 global btstack_root 60 defines = dict() 61 defines.update(read_defines(btstack_root + '/' + hci_cmds_h_path)) 62 defines.update(read_defines(btstack_root + '/' + hci_h_path)) 63 defines.update(read_defines(btstack_root + '/' + bluetooth_h_path)) 64 defines.update(read_defines(btstack_root + '/' + btstack_defines_h_path)) 65 return defines 66 67def my_parse_events(path): 68 events = [] 69 subevents = [] 70 params = [] 71 event_types = set() 72 format = None 73 with open (path, 'rt') as fin: 74 for line in fin: 75 parts = re.match(r'.*@format\s*([a-zA-Z0-9_' + re.escape(open_bracket) + re.escape(closing_bracket) + r']*)\s*', line) 76 if parts and len(parts.groups()) == 1: 77 format = parts.groups()[0] 78 parts = re.match(r'.*@param\s*(\w*)\s*', line) 79 if parts and len(parts.groups()) == 1: 80 param = parts.groups()[0] 81 params.append(param) 82 parts = re.match(r'\s*#define\s+(\w+)\s+(\w*)[u]',line) 83 if parts and len(parts.groups()) == 2: 84 (key, value) = parts.groups() 85 if format != None: 86 # renaming needed by Java Binding (... subevents are just enumerated with others due to event factory) 87 if "_subevent_" in key.lower(): 88 subevents.append((value, key, format, params)) 89 else: 90 events.append((value, key, format, params)) 91 event_types.add(key) 92 params = [] 93 format = None 94 return (events, subevents, event_types) 95 96def parse_events(): 97 global btstack_root 98 99 # parse bluetooth.h to get used events 100 (bluetooth_events, bluetooth_subevents, bluetooth_event_types) = my_parse_events(btstack_root + '/' + bluetooth_h_path) 101 102 # parse btstack_defines to get events 103 (btstack_events, btstack_subevents, btstack_event_types) = my_parse_events(btstack_root + '/' + btstack_defines_h_path) 104 105 # concat lists 106 (events, subvents, event_types) = (bluetooth_events + btstack_events, bluetooth_subevents + btstack_subevents, bluetooth_event_types | btstack_event_types) 107 108 return (events, subvents, event_types) 109def my_parse_opcodes(infile, convert_to_camel_case): 110 opcodes = {} 111 with open (infile, 'rt') as fin: 112 for line in fin: 113 definition = re.match(r'\s*(DAEMON_OPCODE_\w+)\s*=\s*DAEMON_OPCODE\s*\(\s*(\w+)\s*\).*', line) 114 if definition: 115 (opcode, daemon_ocf) = definition.groups() 116 # opcodes.append((opcode, 'OGF_BTSTACK', daemon_ocf)) 117 opcodes[opcode] = ('OGF_BTSTACK', daemon_ocf) 118 definition = re.match(r'\s*(HCI_OPCODE_\w+)\s*=\s*HCI_OPCODE\s*\(\s*(\w+)\s*,\s*(\w+)\s*\).*', line) 119 if definition: 120 (opcode, ogf, ocf) = definition.groups() 121 # opcodes.append((opcode, ogf, ocf)) 122 opcodes[opcode] = (ogf, ocf) 123 return opcodes 124 125def parse_opcodes(camel_case=True): 126 global btstack_root 127 opcodes = {} 128 opcodes.update(my_parse_opcodes(btstack_root + '/' + hci_cmds_h_path, camel_case)) 129 opcodes.update(my_parse_opcodes(btstack_root + '/' + daemon_cmds_h_path, camel_case)) 130 return opcodes 131 132def my_parse_commands(infile, opcodes, convert_to_camel_case): 133 commands = [] 134 with open (infile, 'rt') as fin: 135 136 params = [] 137 for line in fin: 138 139 parts = re.match(r'.*@param\s*(\w*)\s*', line) 140 if parts and len(parts.groups()) == 1: 141 param = parts.groups()[0] 142 if convert_to_camel_case: 143 param = camel_case_var(param) 144 else: 145 param = param.lower() 146 params.append(param) 147 continue 148 149 declaration = re.match(r'const\s+hci_cmd_t\s+(\w+)[\s=]+', line) 150 if declaration: 151 command_name = declaration.groups()[0] 152 # drop _cmd suffix for daemon commands 153 if command_name.endswith('_cmd'): 154 command_name = command_name[:-len('_cmd')] 155 if convert_to_camel_case: 156 command_name = camel_case(command_name) 157 else: 158 command_name = command_name.lower() 159 continue 160 161 # HCI_OPCODE or DAEMON_OPCODE definition 162 definition = re.match(r'\s*(HCI_OPCODE_\w+|DAEMON_OPCODE_\w+)\s*,\s\"(\w*)\".*', line) 163 if definition: 164 (opcode, format) = definition.groups() 165 if len(params) != len(format): 166 params = [] 167 arg_counter = 1 168 for f in format: 169 arg_name = 'arg%u' % arg_counter 170 params.append(arg_name) 171 arg_counter += 1 172 (ogf, ocf) = opcodes[opcode] 173 commands.append((command_name, ogf, ocf, format, params)) 174 params = [] 175 continue 176 177 return commands 178 179def parse_commands(camel_case=True): 180 global btstack_root 181 opcodes = parse_opcodes(camel_case) 182 183 commands = [] 184 commands += my_parse_commands(btstack_root + '/' + hci_cmds_c_path, opcodes, camel_case) 185 commands += my_parse_commands(btstack_root + '/' + daemon_cmds_c_path, opcodes, camel_case) 186 return commands 187 188def parse_daemon_commands(camel_case=True): 189 global btstack_root 190 opcodes = parse_opcodes(camel_case) 191 return my_parse_commands(btstack_root + '/' + daemon_cmds_c_path, opcodes, camel_case) 192 193def print_opcode_enum(commands): 194 print("typedef enum {") 195 for command in commands: 196 (name, ogf, ocf, format, params) = command 197 print(" DAEMON_OPCODE_%s = HCI_OPCODE (%s, %s)," % (name.upper(), ogf, ocf)) 198 print("} daemon_opcode_t;") 199 200 201