Showing posts with label windows. Show all posts
Showing posts with label windows. Show all posts

Saturday, August 30, 2025

Ayaneo Next Lite Stick Calibration, and Other Complaints

A few years ago, I bought my daughter an Ayaneo Next Lite, which is a low-end, but fairly well-made Windows handheld. She loves the device, and uses it a ton to play games, but it has a few issues that annoy me.

First, the machine is pretty darned slow for a device that was released in 2024, at least in the Ryzen 5 4500U version. I've used plenty of Ryzen 5 U-series APUs, and they're usually not particularly slow, so I'm going to chalk this up to thermal limitation. Additionally, the device's storage isn't fast at all. 

Next, the joysticks go out of calibration quite a bit. The fix for this one is fairly straight-forward, but it's not well documented, since very few articles or docs on Ayaneo's site reference the Next Lite, at all. The fix is this:

Press the Select and the A button at the same time for 2 seconds. It vibrates, swirl the joysticks around a couple of times, push RB and throttle 3 times down at the same time and it vibrates again.

That gets the sticks back in service.

Ayaneo also doesn't have a good site, at all. Knowledge is hard to find, and drivers are stored in various places, and it isn't easy to figure out what goes to what.

Oh well, the device was cheap, and my daughter likes it, so I guess that it serves the purpose. 

Wednesday, February 21, 2024

Bluetooth Low-Energy Devices, Windows and Linux

 I've attempted, many times, to get bluetooth devices paired to multiple OSes simultaneously, on the same machine, and never succeeded. Usually, the OSes involved are Windows and Linux, as I primarily use Linux, but use Windows for certain special apps that require it.

The user 'Mygod' posted a really, really useful GitHub gist, a number of years ago, that solves the problem, rather neatly:
https://gist.github.com/Mygod/f390aabf53cf1406fc71166a47236ebf

The only dependencies are:

  1. having your Windows partition mounted somewhere on your Linux system
  2. installing the 'chntpw' package installed

Once you have both of those, download the script from the gist, which I have included here, for convenience:

#!/usr/bin/python3
"""
Copyright 2021 Mygod

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


What is this: Export your Windows Bluetooth LE keys into Linux!

Thanks to:
* http://console.systems/2014/09/how-to-pair-low-energy-le-bluetooth.html
* https://gist.github.com/corecoding/eac76d3da20c7e427a1848b8aed8e334/revisions#diff-6eeb0d27c24cc10680e8574f75648585

Usage:

$ ./export-ble-infos.py 
$ sudo bash -c 'cp -r ./bluetooth /var/lib && service bluetooth force-reload'
$ rm -r bluetooth
"""

import os
import shutil
import subprocess
import sys
import tempfile

from configparser import ConfigParser
from optparse import OptionParser

default_template = """
[General]
Name=Designer Mouse
Appearance=0x03c2
AddressType=static
SupportedTechnologies=LE;
Trusted=true
Blocked=false
Services=00001800-0000-1000-8000-00805f9b34fb;00001801-0000-1000-8000-00805f9b34fb;0000180a-0000-1000-8000-00805f9b34fb;0000180f-0000-1000-8000-00805f9b34fb;00001812-0000-1000-8000-00805f9b34fb;

[IdentityResolvingKey]
Key=

[LocalSignatureKey]
Key=
Counter=0
Authenticated=false

[LongTermKey]
Key=
Authenticated=0
EncSize=16
EDiv=
Rand=

[DeviceID]
Source=2
Vendor=1118
Product=2053
Version=272

[ConnectionParameters]
MinInterval=6
MaxInterval=6
Latency=60
Timeout=300
"""


def main():
    parser = OptionParser()
    parser.add_option("-v", "--verbose", action='store_true', dest='verbose')
    parser.add_option("-s", "--system", dest="system", metavar="FILE",
                      default="/mnt/Windows/System32/config/SYSTEM",
                      help="SYSTEM file in Windows. Usually at /Windows/System32/config/system.")
    parser.add_option("-k", "--key", dest="key", metavar="KEY",
                      default=r"ControlSet001\Services\BTHPORT\Parameters\Keys",
                      help="Registry key for BT. [default: %default]")
    parser.add_option("-o", "--output", dest="output", metavar="DIR", default="bluetooth",
                      help="Output directory. [default: %default]")
    parser.add_option("-t", "--template", dest="template", metavar="FILE", help="Template file.")
    parser.add_option("-a", "--attributes", dest='attributes', help="Additional attributes file to be copied.")
    options, args = parser.parse_args()

    if options.template:
        with open(options.template) as file:
            template = file.read()
    else:
        template = default_template

    out = tempfile.mktemp(".reg")
    reged = subprocess.Popen(["reged", "-x", options.system, '\\', options.key, out], stdout=sys.stderr)
    reged.wait()
    if reged.returncode:
        return reged.returncode
    dump = ConfigParser()
    with open(out) as file:
        reged_out = file.read()
        if options.verbose:
            print(reged_out)
        dump.read_string(reged_out.split('\n', 1)[1])
    os.unlink(out)

    for section in dump:
        path = section[len(options.key) + 2:].split('\\')
        assert not path[0]
        if len(path) == 3:
            path[1] = ':'.join([path[1][i:i + 2] for i in range(0, len(path[1]), 2)]).upper()
            path[2] = ':'.join([path[2][i:i + 2] for i in range(0, len(path[2]), 2)]).upper()
            print("Dumping {}/{}...".format(path[1], path[2]))
            config = ConfigParser()
            config.optionxform = str

            # See if device has been paired in Linux before
            existing_template = '/var/lib/bluetooth/{}/{}/info'.format(path[1], path[2])
            if (os.path.exists(existing_template)):
                with open(existing_template) as file:
                    config.read_string(file.read())
            else:
                config.read_string(template)

            def read_reg(key, expected_type):
                def read_reg_actual(key, expected_type):
                    actual_type, content = dump[section]['"{}"'.format(key)].split(':', 1)
                    if expected_type == 'hex16':
                        assert actual_type == 'hex'
                        content = content.split(',')
                        assert len(content) == 16
                        return ''.join(content).upper()
                    if expected_type == 'qword':
                        assert actual_type == 'hex(b)'
                        content = content.split(',')
                        assert len(content) == 8
                        return str(int(''.join(content[::-1]), 16))
                    if expected_type == 'dword':
                        assert actual_type == expected_type
                        return str(int(content, 16))
                    assert False
                result = read_reg_actual(key, expected_type)
                if options.verbose:
                    print("{} of type {}: {}".format(key, expected_type, result))
                return result
            config['LongTermKey']['Key'] = read_reg('LTK', 'hex16')
            # KeyLength ignored for now
            config['LongTermKey']['Rand'] = read_reg('ERand', 'qword')
            config['LongTermKey']['EDiv'] = read_reg('EDIV', 'dword')
            if '"IRK"' in dump[section]:
                config['IdentityResolvingKey']['Key'] = read_reg('IRK', 'hex16')
            if '"CSRK"' in dump[section]:
                config['LocalSignatureKey']['Key'] = read_reg('CSRK', 'hex16')
            output_dir = os.path.join(options.output, path[1], path[2])
            os.makedirs(output_dir, exist_ok=True)
            with open(os.path.join(output_dir, 'info'), 'w') as file:
                config.write(file, False)
            if options.attributes:
                shutil.copyfile(options.attributes, os.path.join(output_dir, 'attributes'))


if __name__ == "__main__":
    sys.exit(main())

Once you have the script copied to your system, do the following, noting that your keys will be different than mine:

$ chmod 0755 export-ble-infos.py
$ ./export-ble-infos.py -s /PATH TO YOUR WINDOWS PARTITION/Windows/System32/config/SYSTEM
reged version 0.1 140201, (c) Petter N Hagen
Exporting to file '/tmp/tmppe0q46xu.reg'...
Exporting key 'Keys' with 1 subkeys and 0 values...
Exporting key '50284a36a81b' with 1 subkeys and 1 values...
Exporting key 'ffffcfd7dd52' with 0 subkeys and 10 values...
Dumping 50:28:4A:36:A8:1B/FF:FF:CF:D7:DD:52...
$ sudo cp -r bluetooth /var/lib/
$ sudo systemctl force-reload bluetooth
Now, your device should be linked on Linux as well as Windows, when you boot each OS.

Thanks MyGod!

- Alex


Friday, December 18, 2020

Samba and Windows 98

 I've got a Windows 98 host here (yes, really, in 2020!) that I use to read and write floppy disks for my various vintage systems. For those who are curious, it is (at the moment):

Compaq Deskpro EN (chassis only)
Gigabyte GA-6VEM Socket 370 mainboard
Pentium 3 1.4 GHz
1 GB RAM
60 GB SATA2 SSD with IDE/SATA bridge
1.44 MB and 360 kB floppy drives

 I was having issues with the machine being unable to connect to my Fedora-based NAS, receiving the error:

The solution was suggested here, but I needed to add one more configuration item:

[global]
server min protocol = NT1
lanman auth = yes
ntlm auth = yes

That solved the problem and allowed my 98 host to connect and mount my shares. Of course, enabling LANMAN auth is a significant security hole, but on my internal network, I'm not too worried.