mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
feat(data): add LF capture analysis commands
This commit is contained in:
@@ -35,8 +35,8 @@ SRC_FILES += \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/utils/circular_buffer.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/utils/manchester.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/em410x.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/ioprox.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/hidprox.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/ioprox.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/viking.c \
|
||||
$(PROJ_DIR)/rfid/nfctag/lf/protocols/wiegand.c \
|
||||
$(PROJ_DIR)/utils/dataframe.c \
|
||||
@@ -341,13 +341,15 @@ ifeq (${CURRENT_DEVICE_TYPE}, ${CHAMELEON_ULTRA})
|
||||
$(PROJ_DIR)/rfid/reader/hf/rc522.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_125khz_radio.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_em410x_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_em4x05_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_gap.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_reader_generic.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_reader_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_reader_main.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_t55xx_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_ioprox_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_hidprox_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_ioprox_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_viking_data.c \
|
||||
$(PROJ_DIR)/rfid/reader/lf/lf_reader_generic.c \
|
||||
|
||||
INC_FOLDERS +=\
|
||||
${PROJ_DIR}/rfid/reader/ \
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "settings.h"
|
||||
#include "delayed_reset.h"
|
||||
#include "netdata.h"
|
||||
#include "bsp_wdt.h"
|
||||
#include "lf_reader_generic.h"
|
||||
#include "lf_em4x05_data.h"
|
||||
#include "nfc_14a.h"
|
||||
|
||||
|
||||
#define NRF_LOG_MODULE_NAME app_cmd
|
||||
@@ -1729,6 +1733,114 @@ static data_frame_tx_t *cmd_processor_mf0_get_emulator_config(uint16_t cmd, uint
|
||||
* (cmd -> processor) function map, the map struct is:
|
||||
* cmd code before process cmd processor after process
|
||||
*/
|
||||
static data_frame_tx_t *cmd_processor_em4x05_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
em4x05_data_t tag = {0};
|
||||
status = scan_em4x05(&tag);
|
||||
if (status != STATUS_LF_TAG_OK) {
|
||||
return data_frame_make(cmd, status, 0, NULL);
|
||||
}
|
||||
struct {
|
||||
uint32_t config;
|
||||
uint32_t uid;
|
||||
uint32_t uid_hi;
|
||||
uint8_t is_em4x69;
|
||||
} PACKED payload;
|
||||
payload.config = U32HTONL(tag.config);
|
||||
payload.uid = U32HTONL(tag.uid);
|
||||
payload.uid_hi = U32HTONL(tag.uid_hi);
|
||||
payload.is_em4x69 = tag.is_em4x69 ? 1 : 0;
|
||||
return data_frame_make(cmd, STATUS_LF_TAG_OK, sizeof(payload), (uint8_t *)&payload);
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_lf_sniff(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
/* Optional 2-byte big-endian timeout in ms from host (default 2000ms) */
|
||||
uint32_t timeout_ms = 2000;
|
||||
if (length >= 2) {
|
||||
timeout_ms = ((uint32_t)data[0] << 8) | data[1];
|
||||
if (timeout_ms == 0 || timeout_ms > 10000) timeout_ms = 2000;
|
||||
}
|
||||
|
||||
static uint8_t sniff_buf[LF_SNIFF_MAX_SAMPLES];
|
||||
size_t outlen = 0;
|
||||
raw_read_to_buffer(sniff_buf, LF_SNIFF_MAX_SAMPLES, timeout_ms, &outlen);
|
||||
|
||||
if (outlen == 0) {
|
||||
return data_frame_make(cmd, STATUS_LF_TAG_NO_FOUND, 0, NULL);
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_LF_TAG_OK, (uint16_t)outlen, sniff_buf);
|
||||
}
|
||||
#define HF_SNIFF_BUF_SIZE 3800 /* leave room for USB framing */
|
||||
#define HF_SNIFF_MAX_FRAMES 200
|
||||
|
||||
static uint8_t m_sniff_buf[HF_SNIFF_BUF_SIZE];
|
||||
static uint16_t m_sniff_buf_len = 0;
|
||||
static bool m_sniff_active = false;
|
||||
static uint16_t m_sniff_cb_count = 0; /* debug: total callback invocations */
|
||||
|
||||
static void hf14a_sniff_frame_cb(const uint8_t *data, uint16_t szBits) {
|
||||
m_sniff_cb_count++; /* count even if buffer full or inactive */
|
||||
if (!m_sniff_active) return;
|
||||
uint16_t szBytes = (szBits + 7) / 8;
|
||||
/* Check space: 2 bytes header + data */
|
||||
if (m_sniff_buf_len + 2 + szBytes > HF_SNIFF_BUF_SIZE) return;
|
||||
/* Write bit count big-endian */
|
||||
m_sniff_buf[m_sniff_buf_len++] = (szBits >> 8) & 0xFF;
|
||||
m_sniff_buf[m_sniff_buf_len++] = szBits & 0xFF;
|
||||
/* Write frame bytes */
|
||||
memcpy(&m_sniff_buf[m_sniff_buf_len], data, szBytes);
|
||||
m_sniff_buf_len += szBytes;
|
||||
}
|
||||
|
||||
static data_frame_tx_t *cmd_processor_hf14a_sniff(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
|
||||
/* Optional 2-byte big-endian timeout in ms (default 5000ms) */
|
||||
uint32_t timeout_ms = 5000;
|
||||
if (length >= 2) {
|
||||
timeout_ms = ((uint32_t)data[0] << 8) | data[1];
|
||||
if (timeout_ms == 0 || timeout_ms > 30000) timeout_ms = 5000;
|
||||
}
|
||||
|
||||
/* Reload active slot data before sniffing.
|
||||
* The NFCT anti-collision response is built from m_tag_information which
|
||||
* points into the shared tag data buffer. After a slot switch the buffer
|
||||
* may still contain the previous slot's UID if the FDS async load has not
|
||||
* completed. A forced reload here ensures the correct UID is presented
|
||||
* during the sniff session.
|
||||
* A short settle delay follows to allow the reload to complete before
|
||||
* the first field detection can trigger the anti-collision path. */
|
||||
tag_emulation_load_data();
|
||||
bsp_delay_ms(100);
|
||||
|
||||
/* Install sniff callback into the already-running tag emulation stack.
|
||||
* Do NOT call tag_mode_enter() or sense_switch() here — those reinit
|
||||
* NFCT and wipe the anti-collision data, breaking the emulation.
|
||||
* The device must already be in emulator mode (hw mode --emulator)
|
||||
* with a slot active before running this command. */
|
||||
m_sniff_buf_len = 0;
|
||||
m_sniff_cb_count = 0;
|
||||
m_sniff_active = true;
|
||||
nfc_tag_14a_set_sniff_cb(hf14a_sniff_frame_cb);
|
||||
|
||||
/* Wait for duration, yielding each ms so USB stack stays alive.
|
||||
* Feed watchdog every iteration — WDT timeout is 5000ms and the
|
||||
* main loop cannot feed it while we are blocking here. */
|
||||
autotimer *p_at = bsp_obtain_timer(0);
|
||||
while (NO_TIMEOUT_1MS(p_at, timeout_ms)) {
|
||||
bsp_delay_ms(1);
|
||||
bsp_wdt_feed();
|
||||
}
|
||||
bsp_return_timer(p_at);
|
||||
|
||||
/* Remove callback and restore normal sense state */
|
||||
m_sniff_active = false;
|
||||
nfc_tag_14a_clear_sniff_cb();
|
||||
tag_emulation_sense_run(); /* restore slot-based sense state */
|
||||
|
||||
if (m_sniff_buf_len == 0) {
|
||||
return data_frame_make(cmd, STATUS_HF_TAG_NO, 0, NULL);
|
||||
}
|
||||
return data_frame_make(cmd, STATUS_SUCCESS, m_sniff_buf_len, m_sniff_buf);
|
||||
}
|
||||
|
||||
static cmd_data_map_t m_data_cmd_map[] = {
|
||||
{ DATA_CMD_GET_APP_VERSION, NULL, cmd_processor_get_app_version, NULL },
|
||||
{ DATA_CMD_CHANGE_DEVICE_MODE, NULL, cmd_processor_change_device_mode, NULL },
|
||||
@@ -1808,6 +1920,9 @@ static cmd_data_map_t m_data_cmd_map[] = {
|
||||
|
||||
{ DATA_CMD_IOPROX_DECODE_RAW, NULL, cmd_processor_ioprox_decode_raw, NULL },
|
||||
{ DATA_CMD_IOPROX_COMPOSE_ID, NULL, cmd_processor_ioprox_compose_id, NULL },
|
||||
{ DATA_CMD_EM4X05_SCAN, before_reader_run, cmd_processor_em4x05_scan, NULL },
|
||||
{ DATA_CMD_LF_SNIFF, before_reader_run, cmd_processor_lf_sniff, NULL },
|
||||
{ DATA_CMD_HF14A_SNIFF, NULL, cmd_processor_hf14a_sniff, NULL },
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
#define STATUS_LF_TAG_OK (0x40) // Some of the low -frequency cards are successful!
|
||||
#define STATUS_LF_TAG_NO_FOUND (0x41) // Can't search for valid LF tags
|
||||
#define STATUS_LF_TAG_LOGIN_REQUIRED (0x42) // Tag requires LOGIN before read
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// other status
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
#define DATA_CMD_HF14A_GET_CONFIG (2200)
|
||||
#define DATA_CMD_HF14A_SET_CONFIG (2201)
|
||||
#define DATA_CMD_HF14A_SNIFF (2020)
|
||||
|
||||
//
|
||||
// ******************************************************************
|
||||
@@ -173,4 +174,8 @@
|
||||
#define DATA_CMD_IOPROX_SET_EMU_ID (5008)
|
||||
#define DATA_CMD_IOPROX_GET_EMU_ID (5009)
|
||||
|
||||
#define DATA_CMD_EM4X05_SCAN (3030)
|
||||
#define DATA_CMD_EM4X05_READSNIFF (3032)
|
||||
#define DATA_CMD_LF_SNIFF (3031)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -59,6 +59,17 @@ const uint16_t ats_fsdi_table[] = {
|
||||
static volatile bool m_is_responded = false;
|
||||
// Receiving buffer
|
||||
static uint8_t m_nfc_rx_buffer[MAX_NFC_RX_BUFFER_SIZE] = { 0x00 };
|
||||
|
||||
/* Optional sniff callback — fires for every received frame */
|
||||
static nfc_tag_14a_sniff_cb_t m_sniff_cb = NULL;
|
||||
|
||||
void nfc_tag_14a_set_sniff_cb(nfc_tag_14a_sniff_cb_t cb) {
|
||||
m_sniff_cb = cb;
|
||||
}
|
||||
|
||||
void nfc_tag_14a_clear_sniff_cb(void) {
|
||||
m_sniff_cb = NULL;
|
||||
}
|
||||
static uint8_t m_nfc_tx_buffer[MAX_NFC_TX_BUFFER_SIZE] = { 0x00 };
|
||||
// The N -secondary connection needs to use SAK, when the "third 'bit' in SAK is 1 is 1, the logo UID is incomplete
|
||||
static uint8_t m_uid_incomplete_sak[] = { 0x04, 0xda, 0x17 };
|
||||
@@ -326,6 +337,11 @@ void nfc_tag_14a_data_process(uint8_t *p_data) {
|
||||
// Because of this error receiving event caused by this possible interference
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sniff hook — fire before any tag response logic */
|
||||
if (m_sniff_cb != NULL) {
|
||||
m_sniff_cb(p_data, szDataBits);
|
||||
}
|
||||
// Manually draw frame, separate data and strange school inspection
|
||||
#if !NFC_TAG_14A_RX_PARITY_AUTO_DEL_ENABLE
|
||||
if (szDataBits >= 9) {
|
||||
|
||||
@@ -82,6 +82,14 @@ typedef struct {
|
||||
|
||||
// Communication reception function that needs to be implemented
|
||||
typedef void (*nfc_tag_14a_reset_handler_t)(void);
|
||||
|
||||
/* Sniff callback — called for every received frame before the tag handler.
|
||||
* data : raw frame bytes (after parity strip)
|
||||
* szBits : number of bits received */
|
||||
typedef void (*nfc_tag_14a_sniff_cb_t)(const uint8_t *data, uint16_t szBits);
|
||||
|
||||
void nfc_tag_14a_set_sniff_cb(nfc_tag_14a_sniff_cb_t cb);
|
||||
void nfc_tag_14a_clear_sniff_cb(void);
|
||||
typedef void (*nfc_tag_14a_state_handler_t)(uint8_t *data, uint16_t szBits);
|
||||
typedef nfc_tag_14a_coll_res_reference_t *(*nfc_tag_14a_coll_handler_t)(void);
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ void tag_emulation_init(void);
|
||||
void tag_emulation_save(void);
|
||||
|
||||
// Starting and ending of the emulation card
|
||||
void tag_emulation_load_data(void);
|
||||
void tag_emulation_sense_run(void);
|
||||
void tag_emulation_sense_end(void);
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "ble_main.h"
|
||||
#include "nrfx_pwm.h"
|
||||
|
||||
/* Exposed so lf_gap.c can stop the PWM and drive LF_ANT_DRIVER directly
|
||||
* to create clean field gaps without relying on PWM pin release state. */
|
||||
extern nrfx_pwm_t m_pwm;
|
||||
|
||||
void lf_125khz_radio_init(void);
|
||||
void lf_125khz_radio_uninit(void);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "lf_reader_generic.h"
|
||||
#include "lf_reader_data.h"
|
||||
|
||||
#include "bsp_delay.h"
|
||||
#include "bsp_wdt.h"
|
||||
#include "bsp_time.h"
|
||||
#include "circular_buffer.h"
|
||||
#include "lf_125khz_radio.h"
|
||||
@@ -13,15 +15,19 @@
|
||||
#include "nrf_log_default_backends.h"
|
||||
NRF_LOG_MODULE_REGISTER();
|
||||
|
||||
#define CIRCULAR_BUFFER_SIZE (128)
|
||||
/*
|
||||
* Circular buffer for SAADC samples.
|
||||
* Increased from 128 to 512 to reduce overrun risk during USB transfer.
|
||||
* The main loop drains it as fast as possible into the output buffer.
|
||||
*/
|
||||
#define CIRCULAR_BUFFER_SIZE (512)
|
||||
static circular_buffer cb;
|
||||
|
||||
// saadc irq is used to sample ANT GPIO.
|
||||
static void saadc_cb(nrf_saadc_value_t *vals, size_t size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
nrf_saadc_value_t val = vals[i];
|
||||
if (!cb_push_back(&cb, &val)) {
|
||||
return;
|
||||
return; /* buffer full — oldest samples dropped */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,14 +47,20 @@ bool raw_read_to_buffer(uint8_t *data, size_t maxlen, uint32_t timeout_ms, size_
|
||||
init_saadc_hw();
|
||||
start_lf_125khz_radio();
|
||||
|
||||
/* Wait for antenna to settle before capturing.
|
||||
* The LC circuit rings for ~400µs on field startup, then takes
|
||||
* another ~800µs to reach steady state. Skip 2ms to be safe. */
|
||||
bsp_delay_ms(2);
|
||||
|
||||
autotimer *p_at = bsp_obtain_timer(0);
|
||||
while (NO_TIMEOUT_1MS(p_at, timeout_ms) && *outlen < maxlen) {
|
||||
uint16_t val = 0;
|
||||
while (cb_pop_front(&cb, &val) && *outlen < maxlen) {
|
||||
val = val >> 5; // 14 bit ADC to 8 bit value and /2 range
|
||||
data[*outlen] = val > 0xff ? 0xff : val;
|
||||
val = val >> 5; /* 14-bit ADC → 9-bit, then >>5 gives 8-bit */
|
||||
data[*outlen] = val > 0xff ? 0xff : (uint8_t)val;
|
||||
++(*outlen);
|
||||
}
|
||||
bsp_wdt_feed(); /* prevent watchdog reset during long captures */
|
||||
}
|
||||
|
||||
bsp_return_timer(p_at);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Capture raw ADC samples from the LF antenna field.
|
||||
*
|
||||
* The SAADC samples at the PWM period rate (125kHz = 8µs/sample).
|
||||
* Each sample is an 8-bit value (14-bit ADC >> 5, clamped to 0xFF).
|
||||
* A steady carrier reads ~0x80-0x82; a gap reads noticeably lower.
|
||||
*
|
||||
* @param data Output buffer for raw samples
|
||||
* @param maxlen Max bytes to capture (max 4000 for USB frame limit)
|
||||
* @param timeout_ms Stop after this many ms even if buffer not full
|
||||
* @param outlen Actual number of bytes written
|
||||
* @return true on success
|
||||
*/
|
||||
/** Maximum bytes a single raw capture can return (USB frame limit). */
|
||||
#define LF_SNIFF_MAX_SAMPLES 4000
|
||||
|
||||
bool raw_read_to_buffer(uint8_t *data, size_t maxlen, uint32_t timeout_ms, size_t *outlen);
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#include "app_status.h"
|
||||
#include "lf_125khz_radio.h"
|
||||
#if defined(PROJECT_CHAMELEON_ULTRA)
|
||||
#include "lf_em4x05_data.h"
|
||||
#endif
|
||||
#include "lf_reader_data.h"
|
||||
|
||||
void set_scan_tag_timeout(uint32_t ms);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -425,6 +425,22 @@ class ChameleonCMD:
|
||||
i += 14
|
||||
return resp
|
||||
|
||||
def hf14a_sniff(self, timeout_ms: int = 5000):
|
||||
"""
|
||||
Capture ISO14443A reader frames while CU acts as a tag emulator.
|
||||
|
||||
The firmware installs a sniff callback into the HF14A stack for the
|
||||
requested duration, then returns all captured frames packed as:
|
||||
[2 bytes: bit count, big-endian] [N bytes: frame data, ceil(bits/8)] ...
|
||||
|
||||
:param timeout_ms: Listen duration in ms (1-30000, default 5000)
|
||||
:return: Raw response — check .status and .data
|
||||
"""
|
||||
timeout_ms = max(1, min(30000, timeout_ms))
|
||||
payload = bytes([(timeout_ms >> 8) & 0xFF, timeout_ms & 0xFF])
|
||||
timeout_s = (timeout_ms // 1000) + 5
|
||||
return self.device.send_cmd_sync(Command.HF14A_SNIFF, payload, timeout=timeout_s)
|
||||
|
||||
@expect_response(Status.SUCCESS)
|
||||
def hf14a_get_config(self):
|
||||
"""
|
||||
@@ -555,6 +571,48 @@ class ChameleonCMD:
|
||||
resp.parsed = struct.unpack(">BBH8sBBBB", resp.data[:16])
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
def lf_sniff(self, timeout_ms: int = 2000):
|
||||
"""
|
||||
Capture raw LF field ADC samples.
|
||||
|
||||
The ChameleonUltra samples the LF antenna at 125kHz (8µs/sample).
|
||||
Each byte is an 8-bit ADC value: ~0x80 = field on, lower = gap/no field.
|
||||
|
||||
:param timeout_ms: Capture duration in ms (1-10000, default 2000)
|
||||
:return: Raw response object — check .status and .data
|
||||
"""
|
||||
timeout_ms = max(1, min(10000, timeout_ms))
|
||||
payload = bytes([(timeout_ms >> 8) & 0xFF, timeout_ms & 0xFF])
|
||||
timeout_s = (timeout_ms // 1000) + 2
|
||||
return self.device.send_cmd_sync(Command.LF_SNIFF, payload, timeout=timeout_s)
|
||||
|
||||
|
||||
|
||||
@expect_response(Status.LF_TAG_OK)
|
||||
def em4x05_scan(self, pwd: int = 0):
|
||||
"""
|
||||
Read an EM4x05 or EM4x69 tag (reader-talk-first).
|
||||
|
||||
Response payload (14 bytes, big-endian):
|
||||
config 4 bytes — block 0 configuration word
|
||||
uid 4 bytes — EM4x05 UID
|
||||
uid_hi 4 bytes — EM4x69 uid_hi (zero for plain EM4x05)
|
||||
is_em4x69 1 byte — 1 if a 64-bit EM4x69 UID was read
|
||||
uid_block 1 byte — block number UID was read from
|
||||
|
||||
:param pwd: 32-bit password for LOGIN (default 0x00000000)
|
||||
:return: parsed tuple (config, uid, uid_hi, is_em4x69, uid_block)
|
||||
"""
|
||||
pwd_bytes = struct.pack('!I', pwd & 0xFFFFFFFF)
|
||||
resp = self.device.send_cmd_sync(Command.EM4X05_SCAN, pwd_bytes)
|
||||
if resp.status == Status.LF_TAG_OK:
|
||||
resp.parsed = struct.unpack('!IIIBB', resp.data[:14])
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
@expect_response(Status.LF_TAG_OK)
|
||||
def viking_scan(self):
|
||||
"""
|
||||
|
||||
@@ -75,6 +75,7 @@ class Command(enum.IntEnum):
|
||||
MF1_CHECK_KEYS_ON_BLOCK = 2015
|
||||
HF14A_GET_CONFIG = 2200
|
||||
HF14A_SET_CONFIG = 2201
|
||||
HF14A_SNIFF = 2020
|
||||
|
||||
EM410X_SCAN = 3000
|
||||
EM410X_WRITE_TO_T55XX = 3001
|
||||
@@ -143,6 +144,9 @@ class Command(enum.IntEnum):
|
||||
VIKING_GET_EMU_ID = 5005
|
||||
IOPROX_SET_EMU_ID = 5008
|
||||
IOPROX_GET_EMU_ID = 5009
|
||||
EM4X05_SCAN = 3030
|
||||
EM4X05_READSNIFF = 3032
|
||||
LF_SNIFF = 3031
|
||||
|
||||
|
||||
@enum.unique
|
||||
|
||||
Reference in New Issue
Block a user