1
0

header_generator.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. """This script generates the XAP protocol generated header to be compiled into QMK.
  2. """
  3. import re
  4. from fnvhash import fnv1a_32
  5. from qmk.commands import dump_lines
  6. from qmk.git import git_get_version
  7. from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
  8. from qmk.xap.common import latest_xap_defs, route_conditions
  9. def _get_c_type(xap_type):
  10. if xap_type == 'bool':
  11. return 'bool'
  12. elif xap_type == 'u8':
  13. return 'uint8_t'
  14. elif xap_type == 'u16':
  15. return 'uint16_t'
  16. elif xap_type == 'u32':
  17. return 'uint32_t'
  18. elif xap_type == 'u64':
  19. return 'uint64_t'
  20. elif xap_type == 'struct':
  21. return 'struct'
  22. elif xap_type == 'string':
  23. return 'const char *'
  24. return 'unknown'
  25. def _append_route_defines(lines, container, container_id=None, route_stack=None):
  26. """Handles building the list of the XAP routes, combining parent and child names together, as well as the route number.
  27. """
  28. if route_stack is None:
  29. route_stack = [container]
  30. else:
  31. route_stack.append(container)
  32. route_name = '_'.join([r['define'] for r in route_stack])
  33. if container_id:
  34. lines.append(f'#define {route_name} {container_id}')
  35. if 'routes' in container:
  36. for route_id in container['routes']:
  37. route = container['routes'][route_id]
  38. _append_route_defines(lines, route, route_id, route_stack)
  39. route_stack.pop()
  40. def _append_route_masks(lines, container, container_id=None, route_stack=None):
  41. """Handles creating the equivalent XAP route masks, for capabilities checks. Forces value of `0` if disabled in the firmware.
  42. """
  43. if route_stack is None:
  44. route_stack = [container]
  45. else:
  46. route_stack.append(container)
  47. route_name = '_'.join([r['define'] for r in route_stack])
  48. condition = route_conditions(route_stack)
  49. if container_id:
  50. if condition:
  51. lines.append('')
  52. lines.append(f'#if {condition}')
  53. lines.append(f'#define {route_name}_MASK (1ul << ({route_name}))')
  54. if condition:
  55. lines.append(f'#else // {condition}')
  56. lines.append(f'#define {route_name}_MASK 0')
  57. lines.append(f'#endif // {condition}')
  58. lines.append('')
  59. if 'routes' in container:
  60. for route_id in container['routes']:
  61. route = container['routes'][route_id]
  62. _append_route_masks(lines, route, route_id, route_stack)
  63. route_stack.pop()
  64. def _append_route_capabilities(lines, container, container_id=None, route_stack=None):
  65. """Handles creating the equivalent XAP route masks, for capabilities checks. Forces value of `0` if disabled in the firmware.
  66. """
  67. if route_stack is None:
  68. route_stack = [container]
  69. else:
  70. route_stack.append(container)
  71. route_name = '_'.join([r['define'] for r in route_stack])
  72. if 'routes' in container:
  73. lines.append('')
  74. lines.append(f'#define {route_name}_CAPABILITIES (0 \\')
  75. if 'routes' in container:
  76. for route_id in container['routes']:
  77. route = container['routes'][route_id]
  78. route_stack.append(route)
  79. child_name = '_'.join([r['define'] for r in route_stack])
  80. lines.append(f' | ({child_name}_MASK) \\')
  81. route_stack.pop()
  82. lines.append(' )')
  83. if 'routes' in container:
  84. for route_id in container['routes']:
  85. route = container['routes'][route_id]
  86. _append_route_capabilities(lines, route, route_id, route_stack)
  87. route_stack.pop()
  88. def _append_types(lines, container):
  89. """Handles creating the various constants, types, defines, etc.
  90. """
  91. response_flags = container.get('response_flags', {})
  92. prefix = response_flags['define_prefix']
  93. for key, value in response_flags['bits'].items():
  94. define = value.get('define')
  95. lines.append(f'#define {prefix}_{define} (1ul << ({key}))')
  96. # Add special
  97. lines.append(f'#define {prefix}_FAILED 0x00')
  98. lines.append('')
  99. additional_types = {}
  100. types = container.get('type_definitions', {})
  101. for key, value in types.items():
  102. data_type = _get_c_type(value['type'])
  103. additional_types[key] = f'xap_{key}_t'
  104. for key, value in types.items():
  105. data_type = _get_c_type(value['type'])
  106. if data_type == 'struct':
  107. members = value['struct_members']
  108. lines.append(f'typedef {data_type} {{')
  109. for member in members:
  110. member_name = member["name"]
  111. member_type = _get_c_type(member["type"])
  112. if member_type == 'unknown':
  113. member_type = additional_types[member["type"]]
  114. lines.append(f' {member_type} {member_name};')
  115. lines.append(f'}} xap_{key}_t;')
  116. else:
  117. lines.append(f'typedef {data_type} xap_{key}_t;')
  118. def generate_header(output_file, keyboard):
  119. """Generates the XAP protocol header file, generated during normal build.
  120. """
  121. xap_defs = latest_xap_defs()
  122. # Preamble
  123. lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']
  124. # Versions
  125. prog = re.compile(r'^(\d+)\.(\d+)\.(\d+)')
  126. b = prog.match(xap_defs['version'])
  127. lines.append(f'#define XAP_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul')
  128. b = prog.match(git_get_version() or "")
  129. lines.append(f'#define QMK_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul')
  130. keyboard_id = fnv1a_32(bytes(keyboard, 'utf-8'))
  131. lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul')
  132. lines.append('')
  133. # Types
  134. _append_types(lines, xap_defs)
  135. lines.append('')
  136. # Append the route and command defines
  137. _append_route_defines(lines, xap_defs)
  138. lines.append('')
  139. _append_route_masks(lines, xap_defs)
  140. lines.append('')
  141. _append_route_capabilities(lines, xap_defs)
  142. lines.append('')
  143. dump_lines(output_file, lines)