aboutsummaryrefslogtreecommitdiff
path: root/lib/python
diff options
context:
space:
mode:
authorJames Young <18669334+noroadsleft@users.noreply.github.com>2021-05-29 14:38:50 -0700
committerGitHub <noreply@github.com>2021-05-29 14:38:50 -0700
commit1646c0f26cfa21a7023d404008e4d0aa4917193d (patch)
tree337ab0498a929285a234518fee34a4d9dcf51656 /lib/python
parentf55e39e8a2246f6f96fd5d4a84a866e2615cde7b (diff)
downloadqmk_firmware-1646c0f26cfa21a7023d404008e4d0aa4917193d.tar.gz
qmk_firmware-1646c0f26cfa21a7023d404008e4d0aa4917193d.zip
2021 May 29 Breaking Changes Update (#13034)
* Add Per Key functionality for AutoShift (#11536) * LED Matrix: Reactive effect buffers & advanced indicators (#12588) * [Keyboard] kint36: switch to sym_eager_pk debouncing (#12626) * [Keyboard] kint2pp: reduce input latency by ≈10ms (#12625) * LED Matrix: Split (#12633) * [CI] Format code according to conventions (#12650) * feat: infinite timeout for leader key (#6580) * feat: implement leader_no_timeout logic * docs(leader_key): infinite leader timeout docs * Format code according to conventions (#12680) * Update ADC driver for STM32F1xx, STM32F3xx, STM32F4xx (#12403) * Fix default ADC_RESOLUTION for ADCv3 (and ADCv4) Recent ChibiOS update removed ADC_CFGR1_RES_10BIT from the ADCv3 headers (that macro should not have been there, because ADCv3 has CFGR instead of CFGR1). Fix the default value for ADC_RESOLUTION to use ADC_CFGR_RES_10BITS if it is defined (that name is used for ADCv3 and ADCv4). * Update ADC docs to match the actually used resolution ADC driver for ChibiOS actually uses the 10-bit resolution by default (probably to match AVR); fix the documentation accordingly. Also add both ADC_CFGR_RES_10BITS and ADC_CFGR1_RES_10BIT constants (these names differ according to the ADC implementation in the particular MCU). * Fix pinToMux() for B12 and B13 on STM32F3xx Testing on STM32F303CCT6 revealed that the ADC mux values for B12 and B13 pins were wrong. * Add support for all possible analog pins on STM32F1xx Added ADC mux values for pins A0...A7, B0, B1, C0...C5 on STM32F1xx (they are the same at least for STM32F103x8 and larger F103 devices, and also F102, F105, F107 families). Actually tested on STM32F103C8T6 (therefore pins C0...C5 were not tested). Pins F6...F10, which are present on STM32F103x[C-G] in 144-pin packages, cannot be supported at the moment, because those pins are connected only to ADC3, but the ChibiOS ADC driver for STM32F1xx supports only ADC1. * Add support for all possible analog pins on STM32F4xx Added ADC mux values for pins A0...A7, B0, B1, C0...C5 and optionally F3...F10 (if STM32_ADC_USE_ADC3 is enabled). These mux values are apparently the same for all F4xx devices, except some smaller devices may not have ADC3. Actually tested on STM32F401CCU6, STM32F401CEU6, STM32F411CEU6 (using various WeAct “Blackpill” boards); only pins A0...A7, B0, B1 were tested. Pins F3...F10 are inside `#if STM32_ADC_USE_ADC3` because some devices which don't have ADC3 also don't have the GPIOF port, therefore the code which refers to Fx pins does not compile. * Fix STM32F3xx ADC mux table in documentation The ADC driver documentation had some errors in the mux table for STM32F3xx. Fix this table to match the datasheet and the actual code (mux settings for B12 and B13 were also tested on a real STM32F303CCT6 chip). * Add STM32F1xx ADC pins to the documentation * Add STM32F4xx ADC pins to the documentation * Add initial support for tinyuf2 bootloader (when hosted on F411 blackpill) (#12600) * Add support for jumping to tinyuf2 bootloader. Adds blackpill UF2 example. * Update flashing.md * Update chconf.h * Update config.h * Update halconf.h * Update mcuconf.h * eeprom driver: Refactor where eeprom driver initialisation (and EEPROM emulation initialisation) occurs to make it non-target-specific. (#12671) * Add support for MCU = STM32F446 (#12619) * Add support for MCU = STM32F446 * Update platforms/chibios/GENERIC_STM32_F446XE/configs/config.h * Restore mcuconf.h to the one used by RT-STM32F446RE-NUCLEO64 * stm32f446: update mcuconf.h and board.h for 16MHz operation, with USB enabled, and other peripherals disabled. * Format code according to conventions (#12682) * Format code according to conventions (#12687) * Add STM32L433 and L443 support (#12063) * initial L433 commit * change to XC * fix L433 * disable all peripherals * update system and peripheral clocks * 433 change * use its own board files * revert its own board files * l433 specific change * fix stm32l432xx define * remove duplicate #define * fix bootloader jump * move to L443xx and add i2c2, spi2, usart3 to mcuconf.h * move to L443 * move to L443 * fix sdmmc in mcuconf.h * include STM32L443 * add L443 * Include L443 in compatible microcontrollers * Include L443 in compatible microcontrollers * Update config bootloader jump description * Update ChibiOS define reasoning * Update quantum/mcu_selection.mk * fix git conflict * Updated Function96 with V2 files and removed chconf.h and halconf.h (#12613) * Fix bad PR merge for #6580. (#12721) * Change RGB/LED Matrix to use a simple define for USB suspend (#12697) * [CI] Format code according to conventions (#12731) * Fixing transport's led/rgb matrix suspend state logic (#12770) * [CI] Format code according to conventions (#12772) * Fix comment parsing (#12750) * Added OLED fade out support (#12086) * fix some references to bin/qmk that slipped in (#12832) * Resolve a number of warnings in `qmk generate-api` (#12833) * New command: qmk console (#12828) * stash poc * stash * tidy up implementation * Tidy up slightly for review * Tidy up slightly for review * Bodge environment to make tests pass * Refactor away from asyncio due to windows issues * Filter devices * align vid/pid printing * Add hidapi to the installers * start preparing for multiple hid_listeners * udev rules for hid_listen * refactor to move closer to end state * very basic implementation of the threaded model * refactor how vid/pid/index are supplied and parsed * windows improvements * read the report directly when usage page isn't available * add per-device colors, the choice to show names or numbers, and refactor * add timestamps * Add support for showing bootloaders * tweak the color for bootloaders * Align bootloader disconnect with connect color * add support for showing all bootloaders * fix the pyusb check * tweaks * fix exception * hide a stack trace behind -v * add --no-bootloaders option * add documentation for qmk console * Apply suggestions from code review * pyformat * clean up and flesh out KNOWN_BOOTLOADERS * Remove pointless SERIAL_LINK_ENABLE rules (#12846) * Make Swap Hands use PROGMEM (#12284) This converts the array that the Swap Hands feature uses to use PROGMEM, and to read from that array, as such. Since this array never changes at runtime, there is no reason to keep it in memory. Especially for AVR boards, as memory is a precious resource. * Fix another bin/qmk reference (#12856) * [Keymap] Turn OLED off on suspend in soundmonster keymap (#10419) * Fixup build errors on `develop` branch. (#12723) * LED Matrix: Effects! (#12651) * Fix syntax error when compiling for ARM (#12866) * Remove KEYMAP and LAYOUT_kc (#12160) * alias KEYMAP to LAYOUT * remove KEYMAP and LAYOUT_kc * Add setup, clone, and env to the list of commands we allow even with broken modules (#12868) * Rename `point_t` -> `led_point_t` (#12864) * [Keyboard] updated a vendor name / fixed minor keymap issues (#12881) * Add missing LED Matrix suspend code to suspend.c (#12878) * LED Matrix: Documentation (#12685) * Deprecate `send_unicode_hex_string()` (#12602) * Fix spelling mistake regarding LED Matrix in split_common. (#12888) * [Keymap] Fix QWERTY/DVORAK status output for kzar keymap (#12895) * Use milc.subcommand.config instead of qmk.cli.config (#12915) * Use milc.subcommand.config instead * pyformat * remove the config test * Add function to allow repeated blinking of one layer (#12237) * Implement function rgblight_blink_layer_repeat to allow repeated blinking of one layer at a time * Update doc * Rework rgblight blinking according to requested change * optimize storage * Fixup housekeeping from being invoked twice per loop. (#12933) * matrix: wait for row signal to go HIGH for every row (#12945) I noticed this discrepancy (last row of the matrix treated differently than the others) when optimizing the input latency of my keyboard controller, see also https://michael.stapelberg.ch/posts/2021-05-08-keyboard-input-latency-qmk-kinesis/ Before this commit, when tuning the delays I noticed ghost key presses when pressing the F2 key, which is on the last row of the keyboard matrix: the dead_grave key, which is on the first row of the keyboard matrix, would be incorrectly detected as pressed. After this commit, all keyboard matrix rows are interpreted correctly. I suspect that my setup is more susceptible to this nuance than others because I use GPIO_INPUT_PIN_DELAY=0 and hence don’t have another delay that might mask the problem. * ensure we do not conflict with existing keymap aliases (#12976) * Add support for up to 4 IS31FL3733 drivers (#12342) * Convert Encoder callbacks to be boolean functions (#12805) * [Keyboard] Fix Terrazzo build failure (#12977) * Do not hard set config in CPTC files (#11864) * [Keyboard] Corne - Remove legacy revision support (#12226) * [Keymap] Update to Drashna keymap and user code (based on develop) (#12936) * Add Full-duplex serial driver for ARM boards (#9842) * Document LED_MATRIX_FRAMEBUFFER_EFFECTS (#12987) * Backlight: add defines for default level and breathing state (#12560) * Add dire message about LUFA mass storage bootloader (#13014) * [Keyboard] Remove redundant legacy and common headers for crkbd (#13023) Was causing compiler errors on some systems. * Fix keyboards/keymaps for boolean encoder callback changes (#12985) * `backlight.c`: include `eeprom.h` (#13024) * Add changelog for 2021-05-29 Breaking Changes merge (#12939) * Add ChangeLog for 2021-05-29 Breaking Changes Merge: initial version * Add recent develop changes * Sort recent develop changes * Remove sections for ChibiOS changes per tzarc No ChibiOS changes this round. * Add and sort recent develop changes * add notes about keyboard moves/deletions * import changelog for PR 12172 Documents the change to BOOTMAGIC_ENABLE. * update section headings * re-sort changelog * add additional note regarding Bootmagic changes * remove changelog timestamp * update dates in main Breaking Changes docs * fix broken section anchors in previous changelogs * add link to backlight/eeprom patch to changelog * highlight some more changes * link PRs from section headers * Restore standard readme * run: qmk cformat --core-only
Diffstat (limited to 'lib/python')
-rw-r--r--lib/python/qmk/c_parse.py2
-rw-r--r--lib/python/qmk/cli/__init__.py70
-rw-r--r--lib/python/qmk/cli/console.py302
-rwxr-xr-xlib/python/qmk/cli/generate/layouts.py4
-rw-r--r--lib/python/qmk/commands.py1
-rw-r--r--lib/python/qmk/constants.py2
-rw-r--r--lib/python/qmk/tests/test_cli_commands.py4
7 files changed, 352 insertions, 33 deletions
diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py
index d4f39c883..991373d56 100644
--- a/lib/python/qmk/c_parse.py
+++ b/lib/python/qmk/c_parse.py
@@ -8,7 +8,7 @@ from milc import cli
8from qmk.comment_remover import comment_remover 8from qmk.comment_remover import comment_remover
9 9
10default_key_entry = {'x': -1, 'y': 0, 'w': 1} 10default_key_entry = {'x': -1, 'y': 0, 'w': 1}
11single_comment_regex = re.compile(r' */[/*].*$') 11single_comment_regex = re.compile(r'\s+/[/*].*$')
12multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE) 12multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
13 13
14 14
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index d07790d11..48812ae4b 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -12,6 +12,20 @@ from subprocess import run
12from milc import cli, __VERSION__ 12from milc import cli, __VERSION__
13from milc.questions import yesno 13from milc.questions import yesno
14 14
15import_names = {
16 # A mapping of package name to importable name
17 'pep8-naming': 'pep8ext_naming',
18 'pyusb': 'usb.core',
19}
20
21safe_commands = [
22 # A list of subcommands we always run, even when the module imports fail
23 'clone',
24 'config',
25 'env',
26 'setup',
27]
28
15 29
16def _run_cmd(*command): 30def _run_cmd(*command):
17 """Run a command in a subshell. 31 """Run a command in a subshell.
@@ -50,8 +64,8 @@ def _find_broken_requirements(requirements):
50 module_import = module_name.replace('-', '_') 64 module_import = module_name.replace('-', '_')
51 65
52 # Not every module is importable by its own name. 66 # Not every module is importable by its own name.
53 if module_name == "pep8-naming": 67 if module_name in import_names:
54 module_import = "pep8ext_naming" 68 module_import = import_names[module_name]
55 69
56 if not find_spec(module_import): 70 if not find_spec(module_import):
57 broken_modules.append(module_name) 71 broken_modules.append(module_name)
@@ -107,32 +121,31 @@ if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
107 121
108# Check to make sure we have all our dependencies 122# Check to make sure we have all our dependencies
109msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.' 123msg_install = 'Please run `python3 -m pip install -r %s` to install required python dependencies.'
110 124args = sys.argv[1:]
111if _broken_module_imports('requirements.txt'): 125while args and args[0][0] == '-':
112 if yesno('Would you like to install the required Python modules?'): 126 del args[0]
113 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt') 127
114 else: 128if not args or args[0] not in safe_commands:
115 print() 129 if _broken_module_imports('requirements.txt'):
116 print(msg_install % (str(Path('requirements.txt').resolve()),)) 130 if yesno('Would you like to install the required Python modules?'):
117 print() 131 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt')
118 exit(1) 132 else:
119 133 print()
120if cli.config.user.developer: 134 print(msg_install % (str(Path('requirements.txt').resolve()),))
121 args = sys.argv[1:] 135 print()
122 while args and args[0][0] == '-': 136 exit(1)
123 del args[0] 137
124 if not args or args[0] != 'config': 138 if cli.config.user.developer and _broken_module_imports('requirements-dev.txt'):
125 if _broken_module_imports('requirements-dev.txt'): 139 if yesno('Would you like to install the required developer Python modules?'):
126 if yesno('Would you like to install the required developer Python modules?'): 140 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt')
127 _run_cmd(sys.executable, '-m', 'pip', 'install', '-r', 'requirements-dev.txt') 141 elif yesno('Would you like to disable developer mode?'):
128 elif yesno('Would you like to disable developer mode?'): 142 _run_cmd(sys.argv[0], 'config', 'user.developer=None')
129 _run_cmd(sys.argv[0], 'config', 'user.developer=None') 143 else:
130 else: 144 print()
131 print() 145 print(msg_install % (str(Path('requirements-dev.txt').resolve()),))
132 print(msg_install % (str(Path('requirements-dev.txt').resolve()),)) 146 print('You can also turn off developer mode: qmk config user.developer=None')
133 print('You can also turn off developer mode: qmk config user.developer=None') 147 print()
134 print() 148 exit(1)
135 exit(1)
136 149
137# Import our subcommands 150# Import our subcommands
138from . import bux # noqa 151from . import bux # noqa
@@ -142,6 +155,7 @@ from . import chibios # noqa
142from . import clean # noqa 155from . import clean # noqa
143from . import compile # noqa 156from . import compile # noqa
144from milc.subcommand import config # noqa 157from milc.subcommand import config # noqa
158from . import console # noqa
145from . import docs # noqa 159from . import docs # noqa
146from . import doctor # noqa 160from . import doctor # noqa
147from . import fileformat # noqa 161from . import fileformat # noqa
diff --git a/lib/python/qmk/cli/console.py b/lib/python/qmk/cli/console.py
new file mode 100644
index 000000000..45ff0c8be
--- /dev/null
+++ b/lib/python/qmk/cli/console.py
@@ -0,0 +1,302 @@
1"""Acquire debugging information from usb hid devices
2
3cli implementation of https://www.pjrc.com/teensy/hid_listen.html
4"""
5from pathlib import Path
6from threading import Thread
7from time import sleep, strftime
8
9import hid
10import usb.core
11
12from milc import cli
13
14LOG_COLOR = {
15 'next': 0,
16 'colors': [
17 '{fg_blue}',
18 '{fg_cyan}',
19 '{fg_green}',
20 '{fg_magenta}',
21 '{fg_red}',
22 '{fg_yellow}',
23 ],
24}
25
26KNOWN_BOOTLOADERS = {
27 # VID , PID
28 ('03EB', '2FEF'): 'atmel-dfu: ATmega16U2',
29 ('03EB', '2FF0'): 'atmel-dfu: ATmega32U2',
30 ('03EB', '2FF3'): 'atmel-dfu: ATmega16U4',
31 ('03EB', '2FF4'): 'atmel-dfu: ATmega32U4',
32 ('03EB', '2FF9'): 'atmel-dfu: AT90USB64',
33 ('03EB', '2FFA'): 'atmel-dfu: AT90USB162',
34 ('03EB', '2FFB'): 'atmel-dfu: AT90USB128',
35 ('03EB', '6124'): 'Microchip SAM-BA',
36 ('0483', 'DF11'): 'stm32-dfu: STM32 BOOTLOADER',
37 ('16C0', '05DC'): 'USBasp: USBaspLoader',
38 ('16C0', '05DF'): 'bootloadHID: HIDBoot',
39 ('16C0', '0478'): 'halfkay: Teensy Halfkay',
40 ('1B4F', '9203'): 'caterina: Pro Micro 3.3V',
41 ('1B4F', '9205'): 'caterina: Pro Micro 5V',
42 ('1B4F', '9207'): 'caterina: LilyPadUSB',
43 ('1C11', 'B007'): 'kiibohd: Kiibohd DFU Bootloader',
44 ('1EAF', '0003'): 'stm32duino: Maple 003',
45 ('1FFB', '0101'): 'caterina: Polou A-Star 32U4 Bootloader',
46 ('2341', '0036'): 'caterina: Arduino Leonardo',
47 ('2341', '0037'): 'caterina: Arduino Micro',
48 ('239A', '000C'): 'caterina: Adafruit Feather 32U4',
49 ('239A', '000D'): 'caterina: Adafruit ItsyBitsy 32U4 3v',
50 ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
51 ('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
52 ('2A03', '0036'): 'caterina: Arduino Leonardo',
53 ('2A03', '0037'): 'caterina: Arduino Micro',
54 ('314B', '0106'): 'apm32-dfu: APM32 DFU ISP Mode'
55}
56
57
58class MonitorDevice(object):
59 def __init__(self, hid_device, numeric):
60 self.hid_device = hid_device
61 self.numeric = numeric
62 self.device = hid.Device(path=hid_device['path'])
63 self.current_line = ''
64
65 cli.log.info('Console Connected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', hid_device)
66
67 def read(self, size, encoding='ascii', timeout=1):
68 """Read size bytes from the device.
69 """
70 return self.device.read(size, timeout).decode(encoding)
71
72 def read_line(self):
73 """Read from the device's console until we get a \n.
74 """
75 while '\n' not in self.current_line:
76 self.current_line += self.read(32).replace('\x00', '')
77
78 lines = self.current_line.split('\n', 1)
79 self.current_line = lines[1]
80
81 return lines[0]
82
83 def run_forever(self):
84 while True:
85 try:
86 message = {**self.hid_device, 'text': self.read_line()}
87 identifier = (int2hex(message['vendor_id']), int2hex(message['product_id'])) if self.numeric else (message['manufacturer_string'], message['product_string'])
88 message['identifier'] = ':'.join(identifier)
89 message['ts'] = '{style_dim}{fg_green}%s{style_reset_all} ' % (strftime(cli.config.general.datetime_fmt),) if cli.args.timestamp else ''
90
91 cli.echo('%(ts)s%(color)s%(identifier)s:%(index)d{style_reset_all}: %(text)s' % message)
92
93 except hid.HIDException:
94 break
95
96
97class FindDevices(object):
98 def __init__(self, vid, pid, index, numeric):
99 self.vid = vid
100 self.pid = pid
101 self.index = index
102 self.numeric = numeric
103
104 def run_forever(self):
105 """Process messages from our queue in a loop.
106 """
107 live_devices = {}
108 live_bootloaders = {}
109
110 while True:
111 try:
112 for device in list(live_devices):
113 if not live_devices[device]['thread'].is_alive():
114 cli.log.info('Console Disconnected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', live_devices[device])
115 del live_devices[device]
116
117 for device in self.find_devices():
118 if device['path'] not in live_devices:
119 device['color'] = LOG_COLOR['colors'][LOG_COLOR['next']]
120 LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
121 live_devices[device['path']] = device
122
123 try:
124 monitor = MonitorDevice(device, self.numeric)
125 device['thread'] = Thread(target=monitor.run_forever, daemon=True)
126
127 device['thread'].start()
128 except Exception as e:
129 device['e'] = e
130 device['e_name'] = e.__class__.__name__
131 cli.log.error("Could not connect to %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s:%(vendor_id)04X:%(product_id)04X:%(index)d): %(e_name)s: %(e)s", device)
132 if cli.config.general.verbose:
133 cli.log.exception(e)
134 del live_devices[device['path']]
135
136 if cli.args.bootloaders:
137 for device in self.find_bootloaders():
138 if device.address in live_bootloaders:
139 live_bootloaders[device.address]._qmk_found = True
140 else:
141 name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))]
142 cli.log.info('Bootloader Connected: {style_bright}{fg_magenta}%s', name)
143 device._qmk_found = True
144 live_bootloaders[device.address] = device
145
146 for device in list(live_bootloaders):
147 if live_bootloaders[device]._qmk_found:
148 live_bootloaders[device]._qmk_found = False
149 else:
150 name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))]
151 cli.log.info('Bootloader Disconnected: {style_bright}{fg_magenta}%s', name)
152 del live_bootloaders[device]
153
154 sleep(.1)
155
156 except KeyboardInterrupt:
157 break
158
159 def is_bootloader(self, hid_device):
160 """Returns true if the device in question matches a known bootloader vid/pid.
161 """
162 return (int2hex(hid_device.idVendor), int2hex(hid_device.idProduct)) in KNOWN_BOOTLOADERS
163
164 def is_console_hid(self, hid_device):
165 """Returns true when the usage page indicates it's a teensy-style console.
166 """
167 return hid_device['usage_page'] == 0xFF31 and hid_device['usage'] == 0x0074
168
169 def is_filtered_device(self, hid_device):
170 """Returns True if the device should be included in the list of available consoles.
171 """
172 return int2hex(hid_device['vendor_id']) == self.vid and int2hex(hid_device['product_id']) == self.pid
173
174 def find_devices_by_report(self, hid_devices):
175 """Returns a list of available teensy-style consoles by doing a brute-force search.
176
177 Some versions of linux don't report usage and usage_page. In that case we fallback to reading the report (possibly inaccurately) ourselves.
178 """
179 devices = []
180
181 for device in hid_devices:
182 path = device['path'].decode('utf-8')
183
184 if path.startswith('/dev/hidraw'):
185 number = path[11:]
186 report = Path(f'/sys/class/hidraw/hidraw{number}/device/report_descriptor')
187
188 if report.exists():
189 rp = report.read_bytes()
190
191 if rp[1] == 0x31 and rp[3] == 0x09:
192 devices.append(device)
193
194 return devices
195
196 def find_bootloaders(self):
197 """Returns a list of available bootloader devices.
198 """
199 return list(filter(self.is_bootloader, usb.core.find(find_all=True)))
200
201 def find_devices(self):
202 """Returns a list of available teensy-style consoles.
203 """
204 hid_devices = hid.enumerate()
205 devices = list(filter(self.is_console_hid, hid_devices))
206
207 if not devices:
208 devices = self.find_devices_by_report(hid_devices)
209
210 if self.vid and self.pid:
211 devices = list(filter(self.is_filtered_device, devices))
212
213 # Add index numbers
214 device_index = {}
215 for device in devices:
216 id = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id'])))
217
218 if id not in device_index:
219 device_index[id] = 0
220
221 device_index[id] += 1
222 device['index'] = device_index[id]
223
224 return devices
225
226
227def int2hex(number):
228 """Returns a string representation of the number as hex.
229 """
230 return "%04X" % number
231
232
233def list_devices(device_finder):
234 """Show the user a nicely formatted list of devices.
235 """
236 devices = device_finder.find_devices()
237
238 if devices:
239 cli.log.info('Available devices:')
240 for dev in devices:
241 color = LOG_COLOR['colors'][LOG_COLOR['next']]
242 LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
243 cli.log.info("\t%s%s:%s:%d{style_reset_all}\t%s %s", color, int2hex(dev['vendor_id']), int2hex(dev['product_id']), dev['index'], dev['manufacturer_string'], dev['product_string'])
244
245 if cli.args.bootloaders:
246 bootloaders = device_finder.find_bootloaders()
247
248 if bootloaders:
249 cli.log.info('Available Bootloaders:')
250
251 for dev in bootloaders:
252 cli.log.info("\t%s:%s\t%s", int2hex(dev.idVendor), int2hex(dev.idProduct), KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))])
253
254
255@cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.')
256@cli.argument('-d', '--device', help='Device to select - uses format <pid>:<vid>[:<index>].')
257@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available hid_listen devices.')
258@cli.argument('-n', '--numeric', arg_only=True, action='store_true', help='Show VID/PID instead of names.')
259@cli.argument('-t', '--timestamp', arg_only=True, action='store_true', help='Print the timestamp for received messages as well.')
260@cli.argument('-w', '--wait', type=int, default=1, help="How many seconds to wait between checks (Default: 1)")
261@cli.subcommand('Acquire debugging information from usb hid devices.', hidden=False if cli.config.user.developer else True)
262def console(cli):
263 """Acquire debugging information from usb hid devices
264 """
265 vid = None
266 pid = None
267 index = 1
268
269 if cli.config.console.device:
270 device = cli.config.console.device.split(':')
271
272 if len(device) == 2:
273 vid, pid = device
274
275 elif len(device) == 3:
276 vid, pid, index = device
277
278 if not index.isdigit():
279 cli.log.error('Device index must be a number! Got "%s" instead.', index)
280 exit(1)
281
282 index = int(index)
283
284 if index < 1:
285 cli.log.error('Device index must be greater than 0! Got %s', index)
286 exit(1)
287
288 else:
289 cli.log.error('Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "%s".', cli.config.console.device)
290 cli.print_help()
291 exit(1)
292
293 vid = vid.upper()
294 pid = pid.upper()
295
296 device_finder = FindDevices(vid, pid, index, cli.args.numeric)
297
298 if cli.args.list:
299 return list_devices(device_finder)
300
301 print('Looking for devices...', flush=True)
302 device_finder.run_forever()
diff --git a/lib/python/qmk/cli/generate/layouts.py b/lib/python/qmk/cli/generate/layouts.py
index ad6946d6c..4de982f82 100755
--- a/lib/python/qmk/cli/generate/layouts.py
+++ b/lib/python/qmk/cli/generate/layouts.py
@@ -85,7 +85,9 @@ def generate_layouts(cli):
85 85
86 for alias, target in kb_info_json.get('layout_aliases', {}).items(): 86 for alias, target in kb_info_json.get('layout_aliases', {}).items():
87 layouts_h_lines.append('') 87 layouts_h_lines.append('')
88 layouts_h_lines.append('#define %s %s' % (alias, target)) 88 layouts_h_lines.append(f'#ifndef {alias}')
89 layouts_h_lines.append(f'# define {alias} {target}')
90 layouts_h_lines.append('#endif')
89 91
90 # Show the results 92 # Show the results
91 layouts_h = '\n'.join(layouts_h_lines) + '\n' 93 layouts_h = '\n'.join(layouts_h_lines) + '\n'
diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
index ee049e8af..3a35c1103 100644
--- a/lib/python/qmk/commands.py
+++ b/lib/python/qmk/commands.py
@@ -201,6 +201,7 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
201 f'VERBOSE={verbose}', 201 f'VERBOSE={verbose}',
202 f'COLOR={color}', 202 f'COLOR={color}',
203 'SILENT=false', 203 'SILENT=false',
204 f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
204 ]) 205 ])
205 206
206 return make_command 207 return make_command
diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py
index 3ed69f3bf..49e5e0eb4 100644
--- a/lib/python/qmk/constants.py
+++ b/lib/python/qmk/constants.py
@@ -10,7 +10,7 @@ QMK_FIRMWARE = Path.cwd()
10MAX_KEYBOARD_SUBFOLDERS = 5 10MAX_KEYBOARD_SUBFOLDERS = 5
11 11
12# Supported processor types 12# Supported processor types
13CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32G431', 'STM32G474' 13CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66F18', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32L433', 'STM32L443'
14LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None 14LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None
15VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' 15VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'
16 16
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
index a7b70a7d9..afdbc8142 100644
--- a/lib/python/qmk/tests/test_cli_commands.py
+++ b/lib/python/qmk/tests/test_cli_commands.py
@@ -7,7 +7,7 @@ is_windows = 'windows' in platform.platform().lower()
7 7
8 8
9def check_subcommand(command, *args): 9def check_subcommand(command, *args):
10 cmd = ['bin/qmk', command, *args] 10 cmd = ['qmk', command, *args]
11 result = cli.run(cmd, stdin=DEVNULL, combined_output=True) 11 result = cli.run(cmd, stdin=DEVNULL, combined_output=True)
12 return result 12 return result
13 13
@@ -16,7 +16,7 @@ def check_subcommand_stdin(file_to_read, command, *args):
16 """Pipe content of a file to a command and return output. 16 """Pipe content of a file to a command and return output.
17 """ 17 """
18 with open(file_to_read, encoding='utf-8') as my_file: 18 with open(file_to_read, encoding='utf-8') as my_file:
19 cmd = ['bin/qmk', command, *args] 19 cmd = ['qmk', command, *args]
20 result = cli.run(cmd, stdin=my_file, combined_output=True) 20 result = cli.run(cmd, stdin=my_file, combined_output=True)
21 return result 21 return result
22 22