|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
"""Interactions with compatible XAP devices |
|
|
|
|
""" |
|
|
|
|
import cmd |
|
|
|
|
import json |
|
|
|
|
import random |
|
|
|
|
import gzip |
|
|
|
@ -7,6 +8,14 @@ from platform import platform |
|
|
|
|
|
|
|
|
|
from milc import cli |
|
|
|
|
|
|
|
|
|
KEYCODE_MAP = { |
|
|
|
|
# TODO: this should be data driven... |
|
|
|
|
0x04: 'KC_A', |
|
|
|
|
0x05: 'KC_B', |
|
|
|
|
0x29: 'KC_ESCAPE', |
|
|
|
|
0xF9: 'KC_MS_WH_UP', |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _is_xap_usage(x): |
|
|
|
|
return x['usage_page'] == 0xFF51 and x['usage'] == 0x0058 |
|
|
|
@ -170,26 +179,12 @@ def xap_dump_keymap(device): |
|
|
|
|
keycode = _xap_transaction(device, 0x05, 0x02, b"\x00\x00\x00") |
|
|
|
|
|
|
|
|
|
keycode = int.from_bytes(keycode, "little") |
|
|
|
|
keycode_map = { |
|
|
|
|
# TODO: this should be data driven... |
|
|
|
|
0x04: 'KC_A', |
|
|
|
|
0x05: 'KC_B', |
|
|
|
|
0x29: 'KC_ESCAPE', |
|
|
|
|
0xF9: 'KC_MS_WH_UP', |
|
|
|
|
} |
|
|
|
|
print(f'keycode:{keycode_map.get(keycode, "unknown")}[{keycode}]') |
|
|
|
|
print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') |
|
|
|
|
|
|
|
|
|
# set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] |
|
|
|
|
_xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def xap_doit(): |
|
|
|
|
print("xap_doit") |
|
|
|
|
# Reboot |
|
|
|
|
# _xap_transaction(device, 0x01, 0x07) |
|
|
|
|
exit(1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def xap_broadcast_listen(device): |
|
|
|
|
try: |
|
|
|
|
cli.log.info("Listening for XAP broadcasts...") |
|
|
|
@ -208,9 +203,67 @@ def xap_unlock(device): |
|
|
|
|
_xap_transaction(device, 0x00, 0x04) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class XAPShell(cmd.Cmd): |
|
|
|
|
intro = 'Welcome to the XAP shell. Type help or ? to list commands.\n' |
|
|
|
|
prompt = 'Ψ> ' |
|
|
|
|
|
|
|
|
|
def __init__(self, device): |
|
|
|
|
cmd.Cmd.__init__(self) |
|
|
|
|
self.device = device |
|
|
|
|
|
|
|
|
|
def do_about(self, arg): |
|
|
|
|
"""Prints out the current version of QMK with a build date |
|
|
|
|
""" |
|
|
|
|
data = _query_device(self.device) |
|
|
|
|
print(data) |
|
|
|
|
|
|
|
|
|
def do_unlock(self, arg): |
|
|
|
|
"""Initiate secure unlock |
|
|
|
|
""" |
|
|
|
|
xap_unlock(self.device) |
|
|
|
|
print("Done") |
|
|
|
|
|
|
|
|
|
def do_listen(self, arg): |
|
|
|
|
"""Log out XAP broadcast messages |
|
|
|
|
""" |
|
|
|
|
xap_broadcast_listen(self.device) |
|
|
|
|
|
|
|
|
|
def do_keycode(self, arg): |
|
|
|
|
"""Prints out the keycode value of a certain layer, row, and column |
|
|
|
|
""" |
|
|
|
|
data = bytes(map(int, arg.split())) |
|
|
|
|
if len(data) != 3: |
|
|
|
|
cli.log.error("Invalid args") |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
keycode = _xap_transaction(self.device, 0x04, 0x02, data) |
|
|
|
|
keycode = int.from_bytes(keycode, "little") |
|
|
|
|
print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') |
|
|
|
|
|
|
|
|
|
def do_exit(self, line): |
|
|
|
|
"""Quit shell |
|
|
|
|
""" |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def do_EOF(self, line): # noqa: N802 |
|
|
|
|
"""Quit shell (ctrl+D) |
|
|
|
|
""" |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def loop(self): |
|
|
|
|
"""Wrapper for cmdloop that handles ctrl+C |
|
|
|
|
""" |
|
|
|
|
try: |
|
|
|
|
self.cmdloop() |
|
|
|
|
print('') |
|
|
|
|
except KeyboardInterrupt: |
|
|
|
|
print('^C') |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@cli.argument('-d', '--device', help='device to select - uses format <pid>:<vid>.') |
|
|
|
|
@cli.argument('-i', '--index', default=0, help='device index to select.') |
|
|
|
|
@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.') |
|
|
|
|
@cli.argument('-i', '--interactive', arg_only=True, action='store_true', help='Start interactive shell.') |
|
|
|
|
@cli.argument('action', nargs='?', arg_only=True) |
|
|
|
|
@cli.subcommand('Acquire debugging information from usb XAP devices.', hidden=False if cli.config.user.developer else True) |
|
|
|
|
def xap(cli): |
|
|
|
@ -233,16 +286,9 @@ def xap(cli): |
|
|
|
|
device = hid.Device(path=dev['path']) |
|
|
|
|
cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) |
|
|
|
|
|
|
|
|
|
# xap_doit(device) |
|
|
|
|
if cli.args.action == 'unlock': |
|
|
|
|
xap_unlock(device) |
|
|
|
|
cli.log.info("Done") |
|
|
|
|
|
|
|
|
|
elif cli.args.action == 'dump': |
|
|
|
|
xap_dump_keymap(device) |
|
|
|
|
|
|
|
|
|
elif cli.args.action == 'listen': |
|
|
|
|
xap_broadcast_listen(device) |
|
|
|
|
# shell? |
|
|
|
|
if cli.args.interactive: |
|
|
|
|
XAPShell(device).loop() |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
elif not cli.args.action: |
|
|
|
|
xap_broadcast_listen(device) |
|
|
|
|
XAPShell(device).onecmd(cli.args.action or 'listen') |
|
|
|
|