aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach White <skullydazed@gmail.com>2021-05-08 20:56:07 -0700
committerGitHub <noreply@github.com>2021-05-08 20:56:07 -0700
commit7a25dcacffcadf541da5107a35856b66e770bcaf (patch)
treecdb3ee8542ae68b61708f3cab99bbd921e2b8b76
parentd0a3bca9ecc6ccdc75218524b97b9cfb8a681baf (diff)
downloadqmk_firmware-7a25dcacffcadf541da5107a35856b66e770bcaf.tar.gz
qmk_firmware-7a25dcacffcadf541da5107a35856b66e770bcaf.zip
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 Co-authored-by: Ryan <fauxpark@gmail.com> * pyformat * clean up and flesh out KNOWN_BOOTLOADERS Co-authored-by: zvecr <git@zvecr.com> Co-authored-by: Ryan <fauxpark@gmail.com>
-rw-r--r--.github/workflows/cli.yml2
-rwxr-xr-xbin/qmk2
-rw-r--r--docs/cli_commands.md48
-rw-r--r--lib/python/qmk/cli/__init__.py1
-rw-r--r--lib/python/qmk/cli/console.py302
-rw-r--r--requirements-dev.txt2
-rwxr-xr-xutil/install/arch.sh10
-rwxr-xr-xutil/install/debian.sh7
-rwxr-xr-xutil/install/fedora.sh7
-rwxr-xr-xutil/install/gentoo.sh7
-rwxr-xr-xutil/install/msys2.sh9
-rw-r--r--util/udev/50-qmk.rules3
12 files changed, 378 insertions, 22 deletions
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
index 28c6bb367..df727518e 100644
--- a/.github/workflows/cli.yml
+++ b/.github/workflows/cli.yml
@@ -23,6 +23,6 @@ jobs:
23 with: 23 with:
24 submodules: recursive 24 submodules: recursive
25 - name: Install dependencies 25 - name: Install dependencies
26 run: pip3 install -r requirements.txt 26 run: pip3 install -r requirements-dev.txt
27 - name: Run tests 27 - name: Run tests
28 run: bin/qmk pytest 28 run: bin/qmk pytest
diff --git a/bin/qmk b/bin/qmk
index a2af2951c..4b5fd5bbc 100755
--- a/bin/qmk
+++ b/bin/qmk
@@ -33,6 +33,8 @@ def _check_modules(requirements):
33 # Not every module is importable by its own name. 33 # Not every module is importable by its own name.
34 if module['name'] == "pep8-naming": 34 if module['name'] == "pep8-naming":
35 module['import'] = "pep8ext_naming" 35 module['import'] = "pep8ext_naming"
36 elif module['name'] == 'pyusb':
37 module['import'] = 'usb.core'
36 38
37 if not find_spec(module['import']): 39 if not find_spec(module['import']):
38 print('Could not find module %s!' % module['name']) 40 print('Could not find module %s!' % module['name'])
diff --git a/docs/cli_commands.md b/docs/cli_commands.md
index 05e930607..581342093 100644
--- a/docs/cli_commands.md
+++ b/docs/cli_commands.md
@@ -107,6 +107,54 @@ This command lets you configure the behavior of QMK. For the full `qmk config` d
107qmk config [-ro] [config_token1] [config_token2] [...] [config_tokenN] 107qmk config [-ro] [config_token1] [config_token2] [...] [config_tokenN]
108``` 108```
109 109
110## `qmk console`
111
112This command lets you connect to keyboard consoles to get debugging messages. It only works if your keyboard firmware has been compiled with `CONSOLE_ENABLED=yes`.
113
114**Usage**:
115
116```
117qmk console [-d <pid>:<vid>[:<index>]] [-l] [-n] [-t] [-w <seconds>]
118```
119
120**Examples**:
121
122Connect to all available keyboards and show their console messages:
123
124```
125qmk console
126```
127
128List all devices:
129
130```
131qmk console -l
132```
133
134Show only messages from clueboard/66/rev3 keyboards:
135
136```
137qmk console -d C1ED:2370
138```
139
140Show only messages from the second clueboard/66/rev3:
141
142```
143qmk console -d C1ED:2370:2
144```
145
146Show timestamps and VID:PID instead of names:
147
148```
149qmk console -n -t
150```
151
152Disable bootloader messages:
153
154```
155qmk console --no-bootloaders
156```
157
110## `qmk doctor` 158## `qmk doctor`
111 159
112This command examines your environment and alerts you to potential build or flash problems. It can fix many of them if you want it to. 160This command examines your environment and alerts you to potential build or flash problems. It can fix many of them if you want it to.
diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py
index f7df90811..cfb6e6ea5 100644
--- a/lib/python/qmk/cli/__init__.py
+++ b/lib/python/qmk/cli/__init__.py
@@ -12,6 +12,7 @@ from . import chibios
12from . import clean 12from . import clean
13from . import compile 13from . import compile
14from . import config 14from . import config
15from . import console
15from . import docs 16from . import docs
16from . import doctor 17from . import doctor
17from . import fileformat 18from . import fileformat
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/requirements-dev.txt b/requirements-dev.txt
index 1db3b6d73..12d570e70 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -4,5 +4,7 @@
4# Python development requirements 4# Python development requirements
5nose2 5nose2
6flake8 6flake8
7hid
7pep8-naming 8pep8-naming
9pyusb
8yapf 10yapf
diff --git a/util/install/arch.sh b/util/install/arch.sh
index 7442e2f13..eac4ad64e 100755
--- a/util/install/arch.sh
+++ b/util/install/arch.sh
@@ -4,13 +4,13 @@ _qmk_install() {
4 echo "Installing dependencies" 4 echo "Installing dependencies"
5 5
6 sudo pacman --needed --noconfirm -S \ 6 sudo pacman --needed --noconfirm -S \
7 base-devel clang diffutils gcc git unzip wget zip \ 7 base-devel clang diffutils gcc git unzip wget zip python-pip \
8 python-pip \ 8 avr-binutils arm-none-eabi-binutils arm-none-eabi-gcc \
9 avr-binutils \ 9 arm-none-eabi-newlib avrdude dfu-programmer dfu-util
10 arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib \
11 avrdude dfu-programmer dfu-util
12 sudo pacman --needed --noconfirm -U https://archive.archlinux.org/packages/a/avr-gcc/avr-gcc-8.3.0-1-x86_64.pkg.tar.xz 10 sudo pacman --needed --noconfirm -U https://archive.archlinux.org/packages/a/avr-gcc/avr-gcc-8.3.0-1-x86_64.pkg.tar.xz
13 sudo pacman --needed --noconfirm -S avr-libc # Must be installed after the above, or it will bring in the latest avr-gcc instead 11 sudo pacman --needed --noconfirm -S avr-libc # Must be installed after the above, or it will bring in the latest avr-gcc instead
14 12
13 sudo pacman --needed --noconfirm -S hidapi # This will fail if the community repo isn't enabled
14
15 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt 15 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
16} 16}
diff --git a/util/install/debian.sh b/util/install/debian.sh
index 0ae9764a3..ef87c41b5 100755
--- a/util/install/debian.sh
+++ b/util/install/debian.sh
@@ -13,10 +13,9 @@ _qmk_install() {
13 13
14 sudo apt-get -yq install \ 14 sudo apt-get -yq install \
15 build-essential clang-format diffutils gcc git unzip wget zip \ 15 build-essential clang-format diffutils gcc git unzip wget zip \
16 python3-pip \ 16 python3-pip binutils-avr gcc-avr avr-libc binutils-arm-none-eabi \
17 binutils-avr gcc-avr avr-libc \ 17 gcc-arm-none-eabi libnewlib-arm-none-eabi avrdude dfu-programmer \
18 binutils-arm-none-eabi gcc-arm-none-eabi libnewlib-arm-none-eabi \ 18 dfu-util teensy-loader-cli libhidapi-hidraw0
19 avrdude dfu-programmer dfu-util teensy-loader-cli libusb-dev
20 19
21 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt 20 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
22} 21}
diff --git a/util/install/fedora.sh b/util/install/fedora.sh
index 44b71b98b..10fc7c8ad 100755
--- a/util/install/fedora.sh
+++ b/util/install/fedora.sh
@@ -5,11 +5,10 @@ _qmk_install() {
5 5
6 # TODO: Check whether devel/headers packages are really needed 6 # TODO: Check whether devel/headers packages are really needed
7 sudo dnf -y install \ 7 sudo dnf -y install \
8 clang diffutils git gcc glibc-headers kernel-devel kernel-headers make unzip wget zip \ 8 clang diffutils git gcc glibc-headers kernel-devel kernel-headers \
9 python3 \ 9 make unzip wget zip python3 avr-binutils avr-gcc avr-libc \
10 avr-binutils avr-gcc avr-libc \
11 arm-none-eabi-binutils-cs arm-none-eabi-gcc-cs arm-none-eabi-newlib \ 10 arm-none-eabi-binutils-cs arm-none-eabi-gcc-cs arm-none-eabi-newlib \
12 avrdude dfu-programmer dfu-util libusb-devel 11 avrdude dfu-programmer dfu-util hidapi
13 12
14 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt 13 python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
15} 14}
diff --git a/util/install/gentoo.sh b/util/install/gentoo.sh
index 97eb5df07..604d07bf8 100755
--- a/util/install/gentoo.sh
+++ b/util/install/gentoo.sh
@@ -22,9 +22,10 @@ _qmk_install() {
22 echo "sys-devel/gcc multilib" | sudo tee --append /etc/portage/package.use/qmkfirmware >/dev/null 22 echo "sys-devel/gcc multilib" | sudo tee --append /etc/portage/package.use/qmkfirmware >/dev/null
23 sudo emerge -auN sys-devel/gcc 23 sudo emerge -auN sys-devel/gcc
24 sudo emerge -au --noreplace \ 24 sudo emerge -au --noreplace \
25 app-arch/unzip app-arch/zip net-misc/wget sys-devel/clang sys-devel/crossdev \ 25 app-arch/unzip app-arch/zip net-misc/wget sys-devel/clang \
26 \>=dev-lang/python-3.7 \ 26 sys-devel/crossdev \>=dev-lang/python-3.7 dev-embedded/avrdude \
27 dev-embedded/avrdude dev-embedded/dfu-programmer app-mobilephone/dfu-util 27 dev-embedded/dfu-programmer app-mobilephone/dfu-util sys-apps/hwloc \
28 dev-libs/hidapi
28 29
29 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target avr 30 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target avr
30 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target arm-none-eabi 31 sudo crossdev -s4 --stable --g \<9 --portage --verbose --target arm-none-eabi
diff --git a/util/install/msys2.sh b/util/install/msys2.sh
index c8598a60f..9b8343aed 100755
--- a/util/install/msys2.sh
+++ b/util/install/msys2.sh
@@ -9,11 +9,10 @@ _qmk_install() {
9 9
10 pacman --needed --noconfirm --disable-download-timeout -S pactoys-git 10 pacman --needed --noconfirm --disable-download-timeout -S pactoys-git
11 pacboy sync --needed --noconfirm --disable-download-timeout \ 11 pacboy sync --needed --noconfirm --disable-download-timeout \
12 base-devel: toolchain:x clang:x git: unzip: \ 12 base-devel: toolchain:x clang:x git: unzip: python3-pip:x \
13 python3-pip:x \ 13 avr-binutils:x avr-gcc:x avr-libc:x arm-none-eabi-binutils:x \
14 avr-binutils:x avr-gcc:x avr-libc:x \ 14 arm-none-eabi-gcc:x arm-none-eabi-newlib:x avrdude:x bootloadhid:x \
15 arm-none-eabi-binutils:x arm-none-eabi-gcc:x arm-none-eabi-newlib:x \ 15 dfu-programmer:x dfu-util:x teensy-loader-cli:x hidapi:x
16 avrdude:x bootloadhid:x dfu-programmer:x dfu-util:x teensy-loader-cli:x
17 16
18 _qmk_install_drivers 17 _qmk_install_drivers
19 18
diff --git a/util/udev/50-qmk.rules b/util/udev/50-qmk.rules
index acaa7dcc5..679fe4ced 100644
--- a/util/udev/50-qmk.rules
+++ b/util/udev/50-qmk.rules
@@ -60,3 +60,6 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="000e", TAG+="uacc
60SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" 60SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0036", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
61### Micro 61### Micro
62SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1" 62SUBSYSTEMS=="usb", ATTRS{idVendor}=="2a03", ATTRS{idProduct}=="0037", TAG+="uaccess", ENV{ID_MM_DEVICE_IGNORE}="1"
63
64# hid_listen
65KERNEL=="hidraw*", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"