mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
Add TCP and Android (Termux) support
This commit is contained in:
@@ -5,6 +5,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||
## [unreleased][unreleased]
|
||||
- Fix for static nested key recovery (@jekkos)
|
||||
- Fix LEDs being stuck on after battery check (@suut)
|
||||
- Add TCP support for the CLI (@suut)
|
||||
- Fix build on Android in Termux (@suut)
|
||||
|
||||
## [v2.1.0][2025-09-02]
|
||||
- Added UV, formatter and linter. Contribution guidelines. (@GameTec-live)
|
||||
|
||||
@@ -2,8 +2,13 @@ import queue
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
import serial
|
||||
import platform
|
||||
from typing import Union
|
||||
from enum import Enum, auto
|
||||
if platform.system() != 'Android':
|
||||
import serial
|
||||
import socket
|
||||
|
||||
from chameleon_utils import CR, CG, CC, CY, color_string
|
||||
from chameleon_enum import Command, Status
|
||||
|
||||
@@ -13,6 +18,10 @@ THREAD_BLOCKING_TIMEOUT = 0.1
|
||||
# TODO: client settings
|
||||
DEBUG = False
|
||||
|
||||
class TransportType(Enum):
|
||||
NONE = auto()
|
||||
SERIAL = auto()
|
||||
SOCKET = auto()
|
||||
|
||||
class NotOpenException(Exception):
|
||||
"""
|
||||
@@ -57,7 +66,8 @@ class ChameleonCom:
|
||||
"""
|
||||
Create a chameleon device instance
|
||||
"""
|
||||
self.serial_instance: Union[serial.Serial, None] = None
|
||||
self.transport: Union[serial.Serial, socket.socket, None] = None
|
||||
self.transport_type = TransportType.NONE
|
||||
self.send_data_queue = queue.Queue()
|
||||
self.wait_response_map = {}
|
||||
self.event_closing = threading.Event()
|
||||
@@ -68,7 +78,7 @@ class ChameleonCom:
|
||||
|
||||
:return:
|
||||
"""
|
||||
return self.serial_instance is not None and self.serial_instance.is_open
|
||||
return self.transport is not None and (self.transport_type is TransportType.SOCKET or self.transport.is_open)
|
||||
|
||||
def open(self, port) -> "ChameleonCom":
|
||||
"""
|
||||
@@ -82,19 +92,35 @@ class ChameleonCom:
|
||||
error = None
|
||||
try:
|
||||
# open serial port
|
||||
self.serial_instance = serial.Serial(port=port, baudrate=115200)
|
||||
if port.startswith('tcp:'):
|
||||
host, _, port = port[4:].partition(':')
|
||||
if not host or not port:
|
||||
sys.exit(color_string(CR, 'Usage: tcp:127.0.0.1:4321'))
|
||||
self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
print('Connecting to', host, int(port))
|
||||
self.transport.connect((host, int(port)))
|
||||
self.transport_type = TransportType.SOCKET
|
||||
else:
|
||||
if platform.system() == 'Android':
|
||||
sys.exit(color_string(CR, 'COM port is not supported on Android, make a USB-serial to TCP communication bridge'))
|
||||
self.transport = serial.Serial(port=port, baudrate=115200)
|
||||
self.transport_type = TransportType.SERIAL
|
||||
except Exception as e:
|
||||
error = e
|
||||
finally:
|
||||
if error is not None:
|
||||
raise OpenFailException(error)
|
||||
assert self.serial_instance is not None
|
||||
try:
|
||||
self.serial_instance.dtr = True # must make dtr enable
|
||||
except Exception:
|
||||
# not all serial support dtr, e.g. virtual serial over BLE
|
||||
pass
|
||||
self.serial_instance.timeout = THREAD_BLOCKING_TIMEOUT
|
||||
assert self.transport is not None
|
||||
assert self.transport_type is not TransportType.NONE
|
||||
if self.transport_type is TransportType.SERIAL:
|
||||
try:
|
||||
self.transport.dtr = True # must make dtr enable
|
||||
except Exception:
|
||||
# not all serial support dtr, e.g. virtual serial over BLE
|
||||
pass
|
||||
self.transport.timeout = THREAD_BLOCKING_TIMEOUT
|
||||
else: # SOCKET
|
||||
self.transport.settimeout(THREAD_BLOCKING_TIMEOUT)
|
||||
# clear variable
|
||||
self.send_data_queue.queue.clear()
|
||||
self.wait_response_map.clear()
|
||||
@@ -136,12 +162,14 @@ class ChameleonCom:
|
||||
"""
|
||||
self.event_closing.set()
|
||||
try:
|
||||
assert self.serial_instance is not None
|
||||
self.serial_instance.close()
|
||||
assert self.transport is not None
|
||||
if self.transport_type is TransportType.SOCKET:
|
||||
self.transport.shutdown()
|
||||
self.transport.close()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self.serial_instance = None
|
||||
self.transport = None
|
||||
self.wait_response_map.clear()
|
||||
self.send_data_queue.queue.clear()
|
||||
|
||||
@@ -159,16 +187,29 @@ class ChameleonCom:
|
||||
|
||||
while self.isOpen():
|
||||
# receive
|
||||
try:
|
||||
assert self.serial_instance is not None
|
||||
data_bytes = self.serial_instance.read()
|
||||
except Exception as e:
|
||||
if not self.event_closing.is_set():
|
||||
print(f"Serial Error {e}, thread for receiver exit.")
|
||||
self.close()
|
||||
break
|
||||
if len(data_bytes) > 0:
|
||||
assert self.transport_type is not TransportType.NONE
|
||||
if self.transport_type is TransportType.SERIAL:
|
||||
try:
|
||||
assert self.transport is not None
|
||||
data_bytes = bytearray(self.transport.read())
|
||||
except Exception as e:
|
||||
if not self.event_closing.is_set():
|
||||
print(f"Serial Error {e}, thread for receiver exit.")
|
||||
self.close()
|
||||
break
|
||||
else: # SOCKET
|
||||
try:
|
||||
data_bytes = bytearray(self.transport.recv(1024))
|
||||
except socket.timeout:
|
||||
continue
|
||||
except OSError:
|
||||
print(color_string(CR, 'socket closed'))
|
||||
self.transport = None
|
||||
break
|
||||
|
||||
while len(data_bytes) > 0:
|
||||
data_byte = data_bytes[0]
|
||||
data_bytes = data_bytes[1:]
|
||||
data_buffer.append(data_byte)
|
||||
if data_position < struct.calcsize('!BB'): # start of frame + lrc1
|
||||
if data_position == 0:
|
||||
@@ -267,14 +308,25 @@ class ChameleonCom:
|
||||
self.wait_response_map[task_cmd]['start_time'] = start_time
|
||||
self.wait_response_map[task_cmd]['end_time'] = start_time + task_timeout
|
||||
self.wait_response_map[task_cmd]['is_timeout'] = False
|
||||
try:
|
||||
assert self.serial_instance is not None
|
||||
# send to device
|
||||
self.serial_instance.write(task['frame'])
|
||||
except Exception as e:
|
||||
print(f"Serial Error {e}, thread for transfer exit.")
|
||||
self.close()
|
||||
break
|
||||
assert self.transport_type is not TransportType.NONE
|
||||
if self.transport_type == TransportType.SERIAL:
|
||||
try:
|
||||
assert self.transport is not None
|
||||
# send to device
|
||||
self.transport.write(task['frame'])
|
||||
except Exception as e:
|
||||
print(f"Serial Error {e}, thread for transfer exit.")
|
||||
self.close()
|
||||
break
|
||||
else: # SOCKET
|
||||
try:
|
||||
assert self.transport is not None
|
||||
self.transport.sendall(task['frame'])
|
||||
except OSError as e:
|
||||
self.transport = None
|
||||
print(f'Socket error {e}, thread for transfer exit.')
|
||||
self.close()
|
||||
break
|
||||
# update queue status
|
||||
self.send_data_queue.task_done()
|
||||
# disconnect if DFU command has been sent
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
colorama==0.4.6
|
||||
prompt-toolkit==3.0.39
|
||||
+12
-12
@@ -77,7 +77,7 @@ endif()
|
||||
|
||||
|
||||
# --- Platform specific settings ---
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
MESSAGE(STATUS "Run on linux.")
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
@@ -126,7 +126,7 @@ endif()
|
||||
add_executable(nested ${COMMON_FILES} ${NESTED_UTIL} nested.c)
|
||||
target_include_directories(nested PRIVATE ${SRC_DIR})
|
||||
target_link_libraries(nested PRIVATE ${LIBTHREAD}) # Link common thread lib
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(nested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -138,7 +138,7 @@ endif()
|
||||
add_executable(staticnested ${COMMON_FILES} ${NESTED_UTIL} staticnested.c)
|
||||
target_include_directories(staticnested PRIVATE ${SRC_DIR})
|
||||
target_link_libraries(staticnested PRIVATE ${LIBTHREAD}) # Link common thread lib
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(staticnested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -150,7 +150,7 @@ endif()
|
||||
add_executable(darkside ${COMMON_FILES} ${MFKEY_UTIL} darkside.c)
|
||||
target_include_directories(darkside PRIVATE ${SRC_DIR})
|
||||
# darkside doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(darkside PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -161,7 +161,7 @@ endif()
|
||||
add_executable(mfkey32 ${COMMON_FILES} mfkey32.c)
|
||||
target_include_directories(mfkey32 PRIVATE ${SRC_DIR})
|
||||
# mfkey32 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(mfkey32 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -172,7 +172,7 @@ endif()
|
||||
add_executable(mfkey32v2 ${COMMON_FILES} mfkey32v2.c)
|
||||
target_include_directories(mfkey32v2 PRIVATE ${SRC_DIR})
|
||||
# mfkey32v2 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(mfkey32v2 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -183,7 +183,7 @@ endif()
|
||||
add_executable(mfkey64 ${COMMON_FILES} mfkey64.c)
|
||||
target_include_directories(mfkey64 PRIVATE ${SRC_DIR})
|
||||
# mfkey64 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(mfkey64 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -192,7 +192,7 @@ endif()
|
||||
|
||||
add_executable(staticnested_1nt ${COMMON_FILES} staticnested_1nt.c)
|
||||
target_include_directories(staticnested_1nt PRIVATE ${SRC_DIR})
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(staticnested_1nt PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -201,7 +201,7 @@ endif()
|
||||
|
||||
add_executable(staticnested_2x1nt_rf08s ${COMMON_FILES} staticnested_2x1nt_rf08s.c)
|
||||
target_include_directories(staticnested_2x1nt_rf08s PRIVATE ${SRC_DIR})
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(staticnested_2x1nt_rf08s PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -210,7 +210,7 @@ endif()
|
||||
|
||||
add_executable(staticnested_2x1nt_rf08s_1key ${COMMON_FILES} staticnested_2x1nt_rf08s_1key.c)
|
||||
target_include_directories(staticnested_2x1nt_rf08s_1key PRIVATE ${SRC_DIR})
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(staticnested_2x1nt_rf08s_1key PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
@@ -222,7 +222,7 @@ add_executable(mfulc_des_brute mfulc_des_brute.c)
|
||||
target_include_directories(mfulc_des_brute PRIVATE ${SRC_DIR})
|
||||
target_link_libraries(mfulc_des_brute PRIVATE ${LIBTHREAD} OpenSSL::Crypto)
|
||||
target_compile_options(mfulc_des_brute PRIVATE -Wno-deprecated-declarations)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(mfulc_des_brute PRIVATE _GNU_SOURCE)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif()
|
||||
@@ -244,7 +244,7 @@ target_include_directories(hardnested PRIVATE
|
||||
)
|
||||
target_compile_options(hardnested PRIVATE -Wall)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
target_compile_definitions(hardnested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user