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