Skip to content

Commit

Permalink
add configuration handling for linux emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
ahtn committed Jun 7, 2019
1 parent f6a7fee commit aaeaff3
Show file tree
Hide file tree
Showing 23 changed files with 577 additions and 227 deletions.
68 changes: 66 additions & 2 deletions host-software/keyplus-cli
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ class HelpCommand(GenericCommand):


class ProgramCommand(GenericDeviceCommand):
NO_RF_FILE = -1

def __init__(self):
super(ProgramCommand, self).__init__(
'Program layout, rf settings and hex files'
Expand Down Expand Up @@ -530,18 +532,29 @@ class ProgramCommand(GenericDeviceCommand):
' with --merge-hex, generate a hex file with an erase layout.'
)

self.arg_parser.add_argument(
'-D', '--daemon', dest='daemon_conf',
type=str,
help='Program the keyplusd daemon/emulator with the given'
' configuration file.'
)


def task(self, args):
if (
args.layout_file == None and
args.hex_file == None and
args.rf_file == None and
args.new_id == None and
args.merge_hex == None and
args.erase == None
args.erase == None and
args.daemon_conf
):
self.arg_parser.print_help()
exit(EXIT_NO_ERROR)

if args.daemon_conf:
self.task_daemon_conf(args)
if args.merge_hex:
self.task_mereged_hex(args)
elif args.erase:
Expand All @@ -561,13 +574,38 @@ class ProgramCommand(GenericDeviceCommand):
try:
parser_info = KeyplusParserInfo()
parser_info.print_warnings = True

rf_parser_info = KeyplusParserInfo()
rf_parser_info.print_warnings = True
rf_file = args.rf_file

if args.rf_file == ProgramCommand.NO_RF_FILE:
dummy_settings = {
"rf_settings": {
"aes_encryption_key": "00000000000000000000000000000000",
"rf_channel": 0,
"auto_retransmit_count": 0,
"data_rate": "2mbps",
"transmit_power": "0dB",
"pipe0": '0000000000',
"pipe1": '0000000000',
"pipe2": '00',
"pipe3": '00',
"pipe4": '00',
"pipe5": '00',
}
}
rf_parser_info.set_parse_object(
"<NO_RF_FILE>",
dummy_settings
)
rf_file = None


kp_layout = KeyplusLayout()
kp_layout.from_yaml_file(
layout_file = args.layout_file,
rf_file = args.rf_file,
rf_file = rf_file,
parser_info = parser_info,
rf_parser_info = rf_parser_info,
)
Expand Down Expand Up @@ -596,6 +634,32 @@ class ProgramCommand(GenericDeviceCommand):
else:
fw_hex.write_hex_file(sys.stdout)

def task_daemon_conf(self, args):
## Default location to save this config file
# outfile = "/etc/keyplusd/config.yaml"
outfile = "/etc/keyplusd/config.bin"

if args.outfile:
outfile = args.outfile

# set the cmdline args for `task_mereged_hex()`
merge_hex_args = {
"merge_hex": (0x0000, 0x0200, 0x4000),
"outfile" : outfile,
"outfile_format": "bin",
"new_id": 0,
"layout_file": args.daemon_conf,
"rf_file": ProgramCommand.NO_RF_FILE,
"firmware_settings": (
"chip_name=virtual",
"scan_method=virtual",
"max_rows=1"
),
"hex_file": None,
"erase": None,
}

self.task_mereged_hex(argparse.Namespace(**merge_hex_args));

def task_mereged_hex(self, args):
if (
Expand Down
2 changes: 2 additions & 0 deletions host-software/keyplus/chip_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ def get_chip_id_from_name(name):
else:
raise KeyplusSettingsError("Unknown chip name '{}'".format(name))

CHIP_ID_VIRTUAL = CHIP_ID_SPECIAL | 0x0001

CHIP_ID_TABLE = {

CHIP_ID_SPECIAL | 0x0001: _create_virtual(),
Expand Down
3 changes: 3 additions & 0 deletions host-software/keyplus/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from distutils.version import LooseVersion

import keyplus.cdata_types
import keyplus.chip_id
from keyplus.utility import crc16_bytes
from keyplus.constants import *
from keyplus.exceptions import *
Expand Down Expand Up @@ -149,6 +150,8 @@ def __init__(self, device_id, firmware_info):
def get_io_mapper(self):
return self.io_mapper

def is_virtual(self):
return self.firmware_info.chip_id == keyplus.chip_id.CHIP_ID_VIRTUAL

class KeyboardSettingsInfo(keyplus.cdata_types.settings_header_t):
USB_DESC_STRING = 0x03
Expand Down
2 changes: 1 addition & 1 deletion host-software/keyplus/layout/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
| FEATURE_CTRL_WIRED_DISABLE;

class LayoutDevice(object):
def __init__(self, device_id=0, name=None, layout_name=None,
def __init__(self, device_id=0, name="", layout_name=None,
scan_mode=None, layout_id=None, split_device_num=0):
self.device_id = device_id
self.name = name
Expand Down
27 changes: 22 additions & 5 deletions host-software/keyplus/layout/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import math

from keyplus.layout.parser_info import KeyplusParserInfo
from keyplus.layout.device import LayoutDevice
from keyplus.layout.device import LayoutDevice, MATRIX_SCANNER_MODE_VIRTUAL
from keyplus.layout.keyboard_layout import *
from keyplus.layout.rf_settings import *
from keyplus.layout.ekc_data import *
Expand Down Expand Up @@ -95,6 +95,7 @@ def from_json_file(self, layout_file=None, rf_file=None,
layout_file,
rf_file,
parser_info=parser_info,
rf_parser_info=rf_parser_info,
load_method=json.loads,
)

Expand Down Expand Up @@ -275,6 +276,11 @@ def parse_json(self, layout_json=None, rf_json=None, parser_info=None, rf_parser
continue

layout = self.get_layout_by_name(dev.layout)
if layout == None:
raise KeyplusParseError(
"Device '{}' references non-existent layout '{}'"
.format(dev.name, dev.layout)
)
dev_sizes = layout.layer_list[0].device_sizes

if dev.split_device_num >= len(dev_sizes):
Expand Down Expand Up @@ -418,7 +424,10 @@ def get_device(self, dev_id):
return self._devices[dev_id]

def build_settings_section(self, device_target):
device = self.get_device(device_target.device_id)
if device_target.is_virtual():
device = LayoutDevice()
else:
device = self.get_device(device_target.device_id)

settings = settings_t()

Expand All @@ -439,12 +448,20 @@ def build_settings_section(self, device_target):
return settings.to_bytes()

def build_layout_section(self, device_target):
device = self.get_device(device_target.device_id)
result = bytearray()

pin_map = device.scan_mode.generate_pin_mapping(device_target)
if device_target.is_virtual():
device_maps = bytearray()
for (dev_id, dev) in self._devices.items():
if dev.scan_mode.mode == MATRIX_SCANNER_MODE_VIRTUAL:
device_maps += dev.scan_mode.virtual_device_to_bytes(device_target, dev_id)
result += struct.pack("<I", len(device_maps))
result += device_maps
else:
device = self.get_device(device_target.device_id)
pin_map = device.scan_mode.generate_pin_mapping(device_target)
result += pin_map.to_bytes()

result += pin_map.to_bytes()
result += self.ekc_data.to_bytes()
result += self._build_layouts()

Expand Down
2 changes: 1 addition & 1 deletion host-software/keyplus/layout/parser_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def enter(self, field):

def get_current_path(self):
def sanitize_field(field):
if field.isalnum():
if field.isidentifier():
return field
else:
return "'{}'".format(field)
Expand Down
96 changes: 96 additions & 0 deletions host-software/keyplus/layout/scan_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from copy import copy
import re
import sys
import struct

from keyplus.device_info import KeyboardPinMapping
import keyplus.cdata_types
Expand Down Expand Up @@ -63,6 +64,8 @@ def __init__(self):
self.matrix_map = {}
self.matrix_pin_map = {}

self.virtual_device = {}

self._unused_key_numbers = []

self.set_debounce_profile('default')
Expand Down Expand Up @@ -580,6 +583,44 @@ def parse_json(self, json_obj=None, parser_info=None):
elif self.mode in [PIN_GND, PIN_VCC]:
self.direct_wiring_pins = parser_info.try_get("pins", field_type=[list, int])
elif self.mode == VIRTUAL:
def _parse_hex_u16(s):
try:
v = int(s, 16)
if 0 < v > 0xffff:
raise Exception
return v
except:
raise KeyplusParseError("bad VID/PID value")


self.virtual_device["vid"] = parser_info.try_get(
field = "vid",
field_type=[str],
remap_function = _parse_hex_u16,
default = 0
)

self.virtual_device["pid"] = parser_info.try_get(
field = "pid",
field_type=[str],
remap_function = _parse_hex_u16,
default = 0
)

self.virtual_device["name"] = parser_info.try_get(
field = "name",
field_type=[str],
optional = True,
ignore_case = False
)

self.virtual_device["serial"] = parser_info.try_get(
field = "serial",
field_type=[str],
optional = True,
ignore_case = False
)

self.keys = parser_info.try_get("keys", field_type=[list])
else:
raise Exception("Internal error, unexpected value for scan_mode")
Expand Down Expand Up @@ -632,6 +673,61 @@ def parse_json(self, json_obj=None, parser_info=None):

parser_info.exit()

def _virtual_device_header_to_bytes(self, dev_id):
"""
The device header is used to match USB/BT devices that are connected
to see if we should use them.
"""
result = bytearray()

"""
virtula_device_header_t {
char name[64];
char serial[64];
uint8_t dev_id;
uint16_t vid;
uint16_t pid;
uint8_t reserved[123];
}
sizeof(virtual_device_header_t) == 256
"""

if self.virtual_device["name"] != None:
name = self.virtual_device["name"].encode('utf8')[:63]
result += name + bytearray(max(0, 64 - len(name)))
else:
result += bytearray([0xff]*64)

if self.virtual_device["serial"] != None:
serial = self.virtual_device["serial"].encode('utf8')[:63]
result += serial + bytearray(max(0, 63 - len(serial)))
else:
result += bytearray([0xff]*64)

result += struct.pack("<B", dev_id)
result += struct.pack("<H", self.virtual_device["vid"])
result += struct.pack("<H", self.virtual_device["pid"])

result += bytearray(123)

assert(len(result)==256)

return result


def virtual_device_to_bytes(self, target, dev_id):
result = bytearray()

# Device header
result += self._virtual_device_header_to_bytes(dev_id)

# Generate the table that remaps HID codes to keyplus key numbers
pin_map = self.generate_pin_mapping(target)
result += pin_map.to_bytes()

return result


def to_json(self):
result = {}

Expand Down
Loading

0 comments on commit aaeaff3

Please sign in to comment.