MX пре 2 година
родитељ
комит
0e6e3e4757
16 измењених фајлова са 372 додато и 339 уклоњено
  1. 24 84
      README.md
  2. 2 2
      application.fam
  3. BIN
      assets/img/uhf_demo_app.jpg
  4. BIN
      assets/img/uhf_demo_app2.jpg
  5. 0 0
      icons/uhf_10px.png
  6. 1 1
      scenes/uhf_scene_device_info.c
  7. 0 1
      scenes/uhf_scene_read_tag.c
  8. 5 3
      scenes/uhf_scene_verify.c
  9. 3 1
      uhf_app.c
  10. 36 16
      uhf_device.c
  11. 2 11
      uhf_device.h
  12. 142 37
      uhf_module.c
  13. 38 19
      uhf_module.h
  14. 59 0
      uhf_tag.c
  15. 5 0
      uhf_tag.h
  16. 55 164
      uhf_worker.c

+ 24 - 84
README.md

@@ -1,21 +1,34 @@
 # [UHF]RFID App for FlipperZero
 
-![FlipperZero](assets/img/uhf_demo_app.jpg)
+![FlipperZero](assets/img/uhf_demo_app2.jpg)
 
 ## Overview
 
 This repository contains a UHF RFID application developed for FlipperZero, a versatile multi-tool device. The app leverages the YRM100 module to enable UHF RFID functionality.
 
+## What's Changed
+
+- A complete refractor from the concept code to covert to a framework. So that it's easy to refractor for different possible module's. See [module.h](uhf_module.h) for more info.
+- Reading bank now can automatically detect bank size, for cases that PC(protocol control) bits aren't properly written to tag.
+- Can now view tag from saved.
+- Can now write tags from saved.
+- Icon added by [@xMasterX](https://github.com/xMasterX)
+- App is now named `[(Q)M100] UHF RFID`, meaning for M100 and QM100 modules
+
 ## Features
 
 - [x] Read Single UHF RFID tag.
 - [x] View saved UHF RFID tag.
-- [ ] Write Single UHF RFID tag. __(in progress)__
-- [ ] Change Module setting parameters.
-- [ ] Easy-to-use interface on FlipperZero's display.
-    - Extras
-        - [ ] Read multiple tags at once
-        - [ ] View multiple on a list view
+- [x] Write Single UHF RFID tag.
+- [ ] Change Module setting parameters. **(In Progress)**
+  - [ ] Set/Reset Access Password
+  - [ ] Set Kill Password
+  - [ ] Kill Tag
+  - [ ] TBD
+- [ ] Edit/Create new data to write.
+- Extras
+  - [ ] Read multiple tags at once
+  - [ ] View multiple on a list view
 
 ## Requirements
 
@@ -43,88 +56,12 @@ To run this application on FlipperZero, you will need:
 2. Connect the uhf module to the flipper via gpio.
 3. Navigate to the UHF RFID app on FlipperZero's menu.
 4. Currently Reading the EPC tag is the only usable option
-... will further update this page as it development goes
+   ... will further update this page as it development goes
 
 ## Contributions
 
 As this app is still in the development stage, I welcome contributions to this project. If you find any issues or want to enhance the application, feel free to create a pull request.
 
-<!-- ## License
-
-This project is licensed under the [MIT License](link_to_license_file). -->
-
-## Future Plans
-- Code cleanup
-- Build a framework around the chip communication commands
-- Build a proper tag class
-```c
-// Ideal concept
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-typedef struct {
-    int uart_fd; // UART file descriptor or other identifier
-} YRM100_RFID;
-
-void sendCommand(YRM100_RFID *rfid, const uint8_t *command, size_t length) {
-    // Implementation to send the command through UART
-    // Write the command to the UART interface using rfid->uart_fd
-}
-
-// Configuration functions:
-
-void setCommunicationBaudRate(YRM100_RFID *rfid) {
-    uint8_t command[] = {0xBB, 0x00, 0x11, 0x00, 0x02, 0x00, 0xC0, 0xD3, 0x7E};
-    sendCommand(rfid, command, sizeof(command));
-}
-
-void setWorkingArea(YRM100_RFID *rfid, uint8_t area) {
-    uint8_t command[] = {0xBB, 0x00, 0x07, 0x00, 0x01, area, 0x09, 0x7E}; 
-    sendCommand(rfid, command, sizeof(command));
-}
-
-// other method etc ... 
-```
-
-```c
-// Ideal concept
-#include <stdint.h>
-#include <stdlib.h>
-
-typedef struct {
-    uint8_t *killPassword;
-    uint8_t *accessPassword;
-    size_t size;
-} ReservedMemory;
-
-typedef struct {
-    uint8_t *header;
-    uint8_t *filter;
-    uint8_t *partition;
-    uint8_t *companyPrefix;
-    uint8_t *itemReference;
-    size_t size;
-} EPCMemory;
-
-typedef struct {
-    uint8_t *tid;
-    size_t size;
-} TIDMemory;
-
-typedef struct {
-    uint8_t *userMemory;
-    size_t size;
-} UserMemory;
-
-typedef struct {
-    ReservedMemory reserved;
-    EPCMemory epc;
-    TIDMemory tid;
-    UserMemory user;
-} ISO18000_6C_Tag;
-```
-
 ## Disclaimer
 
 - This application is provided as-is and may contain bugs or issues.
@@ -134,6 +71,9 @@ typedef struct {
 ## Extra Resources
 
 - [MagicRF M100&QM100_Firmware_manual_en.pdf](assets/res/MagicRF_M100&QM100_Firmware_manual_en.pdf)
+- [TDS_1_9_Standard.pdf](assets/res/TDS_1_9_Standard.pdf)
+- [M5Stack Docs](https://docs.m5stack.com/en/unit/uhf_rfid)
+- [MagicRF Chip DS](http://www.magicrf.com/product_en.htm)
 
 ## Contact
 

+ 2 - 2
application.fam

@@ -1,6 +1,6 @@
 App(
     appid="uhf_rfid",
-    name="[YRM100] UHF RFID",
+    name="[(Q)M100] UHF RFID",
     apptype=FlipperAppType.EXTERNAL,
     targets=["f7"],
     entry_point="uhf_app_main",
@@ -10,7 +10,7 @@ App(
     ],
     stack_size=4 * 1024,
     order=30,
-    fap_icon="uhf_10px.png",
+    fap_icon="icons/uhf_10px.png",
     fap_category="RFID",
     fap_icon_assets="icons",
     fap_icon_assets_symbol="uhf_rfid",

BIN
assets/img/uhf_demo_app.jpg


BIN
assets/img/uhf_demo_app2.jpg


+ 0 - 0
uhf_10px.png → icons/uhf_10px.png


+ 1 - 1
scenes/uhf_scene_device_info.c

@@ -81,7 +81,7 @@ void change_view_on_event(UHFApp* uhf_app) {
         furi_string_get_cstr(furi_temp_str));
 
     widget_add_string_multiline_element(
-        uhf_app->widget, 3, 24, AlignLeft, AlignTop, FontBatteryPercent, temp_str);
+        uhf_app->widget, 3, 24, AlignLeft, AlignTop, FontKeyboard, temp_str);
 
     widget_add_button_element(
         uhf_app->widget,

+ 0 - 1
scenes/uhf_scene_read_tag.c

@@ -44,6 +44,5 @@ void uhf_scene_read_tag_on_exit(void* ctx) {
     uhf_worker_stop(uhf_app->worker);
     // Clear view
     popup_reset(uhf_app->popup);
-
     uhf_blink_stop(uhf_app);
 }

+ 5 - 3
scenes/uhf_scene_verify.c

@@ -42,6 +42,10 @@ bool uhf_scene_verify_on_event(void* ctx, SceneManagerEvent event) {
                 widget_reset(uhf_app->widget);
                 furi_string_reset(temp_str);
                 uhf_worker_stop(uhf_app->worker);
+                // furi_hal_gpio_write(&gpio_ext, false);
+                // furi_delay_ms(50);
+                // furi_hal_gpio_write(&gpio_ext_pa7, true);
+                // furi_delay_ms(50);
                 uhf_worker_start(
                     uhf_app->worker,
                     UHFWorkerStateVerify,
@@ -113,7 +117,7 @@ bool uhf_scene_verify_on_event(void* ctx, SceneManagerEvent event) {
                     AlignCenter,
                     AlignCenter,
                     FontSecondary,
-                    "Please connect your module.\nPlease refer to the git@frux-c/uhf_rfid for help.");
+                    "Please refer to the git@frux-c/uhf_rfid for help.");
                 widget_add_button_element(
                     uhf_app->widget,
                     GuiButtonTypeLeft,
@@ -138,8 +142,6 @@ void uhf_scene_verify_on_exit(void* ctx) {
     furi_string_free(temp_str);
     // Stop worker
     uhf_worker_stop(uhf_app->worker);
-    // Clear view
-    // popup_reset(uhf_app->popup);
     // clear widget
     widget_reset(uhf_app->widget);
 }

+ 3 - 1
uhf_app.c

@@ -187,13 +187,15 @@ int32_t uhf_app_main(void* ctx) {
 
     // enable 5v pin
     furi_hal_power_enable_otg();
+    // init pin a2
+    // furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
 
     scene_manager_next_scene(uhf_app->scene_manager, UHFSceneVerify);
     view_dispatcher_run(uhf_app->view_dispatcher);
 
     // disable 5v pin
     furi_hal_power_disable_otg();
-
+    // furi_hal_gpio_disable_int_callback()
     // exit app
     uhf_free(uhf_app);
     return 0;

+ 36 - 16
uhf_device.c

@@ -55,23 +55,35 @@ static bool uhf_device_save_file(
         // Reserved bank might be added
         // todo : maybe
         uint32_t temp_arr[1];
+        uint8_t temp_arr2[2];
+        // write pc
+        temp_arr2[0] = (uint8_t)(uhf_tag_get_epc_pc(uhf_tag) >> 8) & 0xFF;
+        temp_arr2[1] = (uint8_t)(uhf_tag_get_epc_pc(uhf_tag) & 0xFF);
+        if(!flipper_format_write_hex(file, UHF_EPC_PC_LABEL, temp_arr2, 2)) break;
+        // write crc
+        temp_arr2[0] = (uint8_t)(uhf_tag_get_epc_crc(uhf_tag) >> 8) & 0xFF;
+        temp_arr2[1] = (uint8_t)(uhf_tag_get_epc_crc(uhf_tag) & 0xFF);
+        if(!flipper_format_write_hex(file, UHF_EPC_CRC_LABEL, temp_arr2, 2)) break;
         // write epc
-        temp_arr[0] = uhf_tag->epc->size;
+        temp_arr[0] = uhf_tag_get_epc_size(uhf_tag);
         if(!flipper_format_write_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break;
         if(!flipper_format_write_hex(
-               file, UHF_EPC_BANK_LABEL, uhf_tag->epc->data, uhf_tag->epc->size))
+               file, UHF_EPC_BANK_LABEL, uhf_tag_get_epc(uhf_tag), uhf_tag_get_epc_size(uhf_tag)))
             break;
         // write tid
-        temp_arr[0] = uhf_tag->tid->size;
+        temp_arr[0] = uhf_tag_get_tid_size(uhf_tag);
         if(!flipper_format_write_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break;
         if(!flipper_format_write_hex(
-               file, UHF_TID_BANK_LABEL, uhf_tag->tid->data, uhf_tag->tid->size))
+               file, UHF_TID_BANK_LABEL, uhf_tag_get_tid(uhf_tag), uhf_tag_get_tid_size(uhf_tag)))
             break;
         // write user
-        temp_arr[0] = uhf_tag->user->size;
+        temp_arr[0] = uhf_tag_get_user_size(uhf_tag);
         if(!flipper_format_write_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break;
         if(!flipper_format_write_hex(
-               file, UHF_USER_BANK_LABEL, uhf_tag->user->data, uhf_tag->user->size))
+               file,
+               UHF_USER_BANK_LABEL,
+               uhf_tag_get_user(uhf_tag),
+               uhf_tag_get_user_size(uhf_tag)))
             break;
         saved = true;
     } while(0);
@@ -99,7 +111,7 @@ static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dia
     FuriString* temp_str;
     temp_str = furi_string_alloc();
     bool deprecated_version = false;
-    UHFTag* uhf_tag = dev->uhf_tag_wrapper->uhf_tag;
+    UHFTag* uhf_tag = uhf_tag_alloc();
     uhf_tag_reset(uhf_tag);
     uint32_t temp_arr[1];
     if(dev->loading_cb) {
@@ -116,25 +128,34 @@ static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dia
             deprecated_version = true;
             break;
         }
+        // read pc
+        uint8_t temp_arr2[2];
+        if(!flipper_format_read_hex(file, UHF_EPC_PC_LABEL, temp_arr2, 2)) break;
+        uhf_tag_set_epc_pc(uhf_tag, (temp_arr2[0] << 8) + temp_arr2[1]);
+        // read crc
+        if(!flipper_format_read_hex(file, UHF_EPC_CRC_LABEL, temp_arr2, 2)) break;
+        uhf_tag_set_epc_crc(uhf_tag, (temp_arr2[0] << 8) + temp_arr2[1]);
         // read epc
         if(!flipper_format_read_uint32(file, UHF_EPC_BANK_LENGTH_LABEL, temp_arr, 1)) break;
-        uhf_tag->epc->size = temp_arr[0];
+        uhf_tag_set_epc_size(uhf_tag, temp_arr[0]);
         if(!flipper_format_read_hex(
-               file, UHF_EPC_BANK_LABEL, uhf_tag->epc->data, uhf_tag->epc->size))
+               file, UHF_EPC_BANK_LABEL, uhf_tag_get_epc(uhf_tag), uhf_tag_get_epc_size(uhf_tag)))
             break;
 
         // read tid
         if(!flipper_format_read_uint32(file, UHF_TID_BANK_LENGTH_LABEL, temp_arr, 1)) break;
-        uhf_tag->tid->size = temp_arr[0];
+        uhf_tag_set_tid_size(uhf_tag, temp_arr[0]);
         if(!flipper_format_read_hex(
-               file, UHF_TID_BANK_LABEL, uhf_tag->tid->data, uhf_tag->tid->size))
+               file, UHF_TID_BANK_LABEL, uhf_tag_get_tid(uhf_tag), uhf_tag_get_tid_size(uhf_tag)))
             break;
-
         // read user
         if(!flipper_format_read_uint32(file, UHF_USER_BANK_LENGTH_LABEL, temp_arr, 1)) break;
-        uhf_tag->user->size = temp_arr[0];
+        uhf_tag_set_user_size(uhf_tag, temp_arr[0]);
         if(!flipper_format_read_hex(
-               file, UHF_USER_BANK_LABEL, uhf_tag->user->data, uhf_tag->user->size))
+               file,
+               UHF_USER_BANK_LABEL,
+               uhf_tag_get_user(uhf_tag),
+               uhf_tag_get_user_size(uhf_tag)))
             break;
 
         parsed = true;
@@ -151,10 +172,9 @@ static bool uhf_device_load_data(UHFDevice* dev, FuriString* path, bool show_dia
             dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
         }
     }
-
+    uhf_tag_wrapper_set_tag(dev->uhf_tag_wrapper, uhf_tag);
     furi_string_free(temp_str);
     flipper_format_free(file);
-
     return parsed;
 }
 

+ 2 - 11
uhf_device.h

@@ -7,21 +7,12 @@
 #include <mbedtls/des.h>
 #include "uhf_tag.h"
 
-// #include "rfal_picopass.h"
-
 #define UHF_DEV_NAME_MAX_LEN 22
-// #define PICOPASS_READER_DATA_MAX_SIZE 64
-// #define PICOPASS_BLOCK_LEN 8
-// #define PICOPASS_MAX_APP_LIMIT 32
-#define UHF_BANK_DOES_NOT_EXIST                                                                   \
-    (uint8_t[]) {                                                                                 \
-        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-            0xFF                                                                                  \
-    }
-
 #define UHF_EPC_BANK_LENGTH_LABEL "EPC_LENGTH"
 #define UHF_TID_BANK_LENGTH_LABEL "TID_LENGTH"
 #define UHF_USER_BANK_LENGTH_LABEL "USER_LENGTH"
+#define UHF_EPC_PC_LABEL "PC"
+#define UHF_EPC_CRC_LABEL "CRC"
 #define UHF_RFU_BANK_LABEL "RFU"
 #define UHF_EPC_BANK_LABEL "EPC"
 #define UHF_TID_BANK_LABEL "TID"

+ 142 - 37
uhf_module.c

@@ -1,7 +1,7 @@
 #include "uhf_module.h"
 #include "uhf_module_cmd.h"
 
-#define DELAY_MS 50
+#define DELAY_MS 100
 
 void rx_callback(UartIrqEvent event, uint8_t data, void* ctx) {
     UNUSED(event);
@@ -68,7 +68,10 @@ uint16_t crc16_genibus(const uint8_t* data, size_t length) {
 }
 
 char* m100_get_hardware_version(M100Module* module) {
-    if(module->info->hw_version != NULL) return module->info->hw_version;
+    if(module->info->hw_version != NULL) {
+        free(module->info->hw_version);
+        module->info->hw_version = NULL;
+    }
     buffer_reset(module->buf);
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
     furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)&CMD_HW_VERSION.cmd[0], CMD_HW_VERSION.length);
@@ -88,7 +91,10 @@ char* m100_get_hardware_version(M100Module* module) {
     return module->info->hw_version;
 }
 char* m100_get_software_version(M100Module* module) {
-    if(module->info->sw_version != NULL) return module->info->sw_version;
+    if(module->info->sw_version != NULL) {
+        free(module->info->sw_version);
+        module->info->sw_version = NULL;
+    }
     buffer_reset(module->buf);
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
     furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)&CMD_SW_VERSION.cmd[0], CMD_SW_VERSION.length);
@@ -108,7 +114,10 @@ char* m100_get_software_version(M100Module* module) {
     return module->info->sw_version;
 }
 char* m100_get_manufacturers(M100Module* module) {
-    if(module->info->manufacturer != NULL) return module->info->manufacturer;
+    if(module->info->manufacturer != NULL) {
+        free(module->info->manufacturer);
+        module->info->manufacturer = NULL;
+    }
     buffer_reset(module->buf);
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
     furi_hal_uart_tx(
@@ -129,7 +138,7 @@ char* m100_get_manufacturers(M100Module* module) {
     return module->info->manufacturer;
 }
 
-UHFTag* m100_send_single_poll(M100Module* module) {
+M100ResponseType m100_send_single_poll(M100Module* module, UHFTag* uhf_tag) {
     buffer_reset(module->buf);
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
     furi_hal_uart_tx(
@@ -137,7 +146,7 @@ UHFTag* m100_send_single_poll(M100Module* module) {
     furi_delay_ms(DELAY_MS);
     uint8_t* data = buffer_get_data(module->buf);
     size_t length = buffer_get_size(module->buf);
-    if(length <= 8 && data[2] == 0xFF) return NULL;
+    if(length <= 8 && data[2] == 0xFF) return M100NoTagResponse;
     uint16_t pc = data[6];
     uint16_t crc = 0;
     // mask out epc length from protocol control
@@ -152,26 +161,16 @@ UHFTag* m100_send_single_poll(M100Module* module) {
     crc <<= 8;
     crc += data[8 + epc_len + 1];
     // validate checksum
-    uint8_t cs = checksum(data + 1, length - 3);
-    for(size_t i = 0; i < length; i++){
-        FURI_LOG_E("m100", "data[%d]=%02X", i, data[i]);
-    }
-    if(cs != data[length - 2]) return NULL;
-    FURI_LOG_E("m100", "checksum pass");
+    if(checksum(data + 1, length - 3) != data[length - 2]) return M100ValidationFail;
     // validate crc
-    uint16_t ccrc = crc16_genibus(data + 6, epc_len + 2);
-    FURI_LOG_E("m100", "crc found = %04X, calculated crc = %04X", crc, ccrc);
-    if(ccrc != crc) return NULL;
-    FURI_LOG_E("m100", "crc pass");
-    UHFTag* uhf_tag = uhf_tag_alloc();
+    if(crc16_genibus(data + 6, epc_len + 2) != crc) return M100ValidationFail;
     uhf_tag_set_epc_pc(uhf_tag, pc);
     uhf_tag_set_epc_crc(uhf_tag, crc);
     uhf_tag_set_epc(uhf_tag, data + 8, epc_len);
-    FURI_LOG_E("m100", "returning tag");
-    return uhf_tag;
+    return M100Success;
 }
 
-bool m100_set_select(M100Module* module, UHFTag* uhf_tag) {
+M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag) {
     buffer_reset(module->buf);
     // Set select
     uint8_t cmd[MAX_BUFFER_SIZE];
@@ -181,6 +180,8 @@ bool m100_set_select(M100Module* module, UHFTag* uhf_tag) {
     // payload len = sel param len + ptr len + mask len + epc len
     size_t payload_len = 7 + mask_length_bytes;
     memcpy(cmd, CMD_SET_SELECT_PARAMETER.cmd, cmd_length);
+    // set new length
+    cmd_length = 12 + mask_length_bytes + 2;
     // set payload length
     cmd[3] = (payload_len >> 8) & 0xFF;
     cmd[4] = payload_len & 0xFF;
@@ -194,36 +195,51 @@ bool m100_set_select(M100Module* module, UHFTag* uhf_tag) {
     cmd[11] = false;
     // set mask
     memcpy((void*)&cmd[12], uhf_tag->epc->data, mask_length_bytes);
+
     // set checksum
-    cmd[12 + mask_length_bytes + 1] = checksum(cmd + 1, 11 + mask_length_bytes);
+    cmd[cmd_length - 2] = checksum(cmd + 1, 11 + mask_length_bytes);
     // end frame
-    cmd[12 + mask_length_bytes + 2] = FRAME_END;
+    cmd[cmd_length - 1] = FRAME_END;
     furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, rx_callback, module->buf);
     furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, 12 + mask_length_bytes + 3);
     furi_delay_ms(DELAY_MS);
 
     uint8_t* data = buffer_get_data(module->buf);
-    if(checksum(data + 1, 5) != data[6]) return false; // error in rx
-    if(data[5] != 0x00) return false; // error if not 0
+    if(checksum(data + 1, 5) != data[6]) return M100ValidationFail; // error in rx
+    if(data[5] != 0x00) return M100ValidationFail; // error if not 0
 
-    return true;
+    return M100Success;
 }
 
-UHFTag* m100_get_select_param(M100Module module) {
-    UNUSED(module);
+UHFTag* m100_get_select_param(M100Module* module) {
+    buffer_reset(module->buf);
+    furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, rx_callback, module->buf);
+    furi_hal_uart_tx(
+        FuriHalUartIdUSART1,
+        (uint8_t*)&CMD_GET_SELECT_PARAMETER.cmd,
+        CMD_GET_SELECT_PARAMETER.length);
+    furi_delay_ms(DELAY_MS);
+    // UHFTag* uhf_tag = uhf_tag_alloc();
+    // uint8_t* data = buffer_get_data(module->buf);
+    // size_t mask_length =
+    // uhf_tag_set_epc(uhf_tag, data + 12, )
     return NULL;
 }
 
-bool m100_read_label_data_storage(
+M100ResponseType m100_read_label_data_storage(
     M100Module* module,
     UHFTag* uhf_tag,
     BankType bank,
-    uint32_t access_pwd) {
-    UNUSED(uhf_tag);
+    uint32_t access_pwd,
+    uint16_t word_count) {
+    /*
+    Will probably remove UHFTag as param and get it from get selected tag
+        */
+    if(bank == EPCBank) return M100Success;
     buffer_reset(module->buf);
     uint8_t cmd[MAX_BUFFER_SIZE];
-    size_t length = CMD_READ_LABEL_DATA_STORAGE_AREA.length;
-    memcpy(cmd, CMD_READ_LABEL_DATA_STORAGE_AREA.cmd, length);
+    size_t cmd_length = CMD_READ_LABEL_DATA_STORAGE_AREA.length;
+    memcpy(cmd, CMD_READ_LABEL_DATA_STORAGE_AREA.cmd, cmd_length);
     // set access password
     cmd[5] = (access_pwd >> 24) & 0xFF;
     cmd[6] = (access_pwd >> 16) & 0xFF;
@@ -231,14 +247,104 @@ bool m100_read_label_data_storage(
     cmd[8] = access_pwd & 0xFF;
     // set mem bank
     cmd[9] = (uint8_t)bank;
-    // recalc checksum
-    cmd[length - 2] = checksum(cmd + 1, length - 3);
+    // set word counter
+    cmd[12] = (word_count >> 8) & 0xFF;
+    cmd[13] = word_count & 0xFF;
+    // calc checksum
+    cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3);
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
-    furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, length);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, cmd_length);
     furi_delay_ms(DELAY_MS);
-    return true;
+    uint8_t* data = buffer_get_data(module->buf);
+    uint16_t payload_len = data[3];
+    payload_len = (payload_len << 8) + data[4];
+    size_t ptr_offset = 5 /*<-ptr offset*/ + uhf_tag->epc->size + 3 /*<-pc + ul*/;
+    size_t bank_data_length = payload_len - (ptr_offset - 5 /*dont include the offset*/);
+    if(data[2] == 0xFF) {
+        if(payload_len == 0x0001) return M100NoTagResponse;
+        return M100MemoryOverrun;
+    }
+    if(bank == TIDBank) {
+        uhf_tag_set_tid(uhf_tag, data + ptr_offset, bank_data_length);
+    } else if(bank == UserBank) {
+        uhf_tag_set_user(uhf_tag, data + ptr_offset, bank_data_length);
+    }
+    return M100Success;
 }
 
+M100ResponseType m100_write_label_data_storage(
+    M100Module* module,
+    UHFTag* saved_tag,
+    UHFTag* selected_tag,
+    BankType bank,
+    uint16_t source_address,
+    uint32_t access_pwd) {
+    buffer_reset(module->buf);
+    uint8_t cmd[MAX_BUFFER_SIZE];
+    size_t cmd_length = CMD_WRITE_LABEL_DATA_STORE.length;
+    memcpy(cmd, CMD_WRITE_LABEL_DATA_STORE.cmd, cmd_length);
+    uint16_t payload_len = 9;
+    uint16_t data_length = 0;
+    if(bank == ReservedBank) {
+        // access pwd len + kill pwd len
+        payload_len += 4;
+        data_length = 4;
+    } else if(bank == EPCBank) {
+        // epc len + pc len
+        payload_len += 4 + uhf_tag_get_epc_size(saved_tag);
+        data_length = 4 + uhf_tag_get_epc_size(saved_tag);
+        // set data
+        uint8_t tmp_arr[4];
+        tmp_arr[0] = (uint8_t)((uhf_tag_get_epc_crc(selected_tag) >> 8) & 0xFF);
+        tmp_arr[1] = (uint8_t)(uhf_tag_get_epc_crc(selected_tag) & 0xFF);
+        tmp_arr[2] = (uint8_t)((uhf_tag_get_epc_pc(saved_tag) >> 8) & 0xFF);
+        tmp_arr[3] = (uint8_t)(uhf_tag_get_epc_pc(saved_tag) & 0xFF);
+        memcpy(cmd + 14, tmp_arr, 4);
+        memcpy(cmd + 18, uhf_tag_get_epc(saved_tag), uhf_tag_get_epc_size(saved_tag));
+    } else if(bank == UserBank) {
+        payload_len += uhf_tag_get_user_size(saved_tag);
+        data_length = uhf_tag_get_user_size(saved_tag);
+        // set data
+        memcpy(cmd + 14, uhf_tag_get_user(saved_tag), uhf_tag_get_user_size(saved_tag));
+    }
+    // set payload length
+    cmd[3] = (payload_len >> 8) & 0xFF;
+    cmd[4] = payload_len & 0xFF;
+    // set access password
+    cmd[5] = (access_pwd >> 24) & 0xFF;
+    cmd[6] = (access_pwd >> 16) & 0xFF;
+    cmd[7] = (access_pwd >> 8) & 0xFF;
+    cmd[8] = access_pwd & 0xFF;
+    // set membank
+    cmd[9] = (uint8_t)bank;
+    // set source address
+    cmd[10] = (source_address >> 8) & 0xFF;
+    cmd[11] = source_address & 0xFF;
+    // set data length
+    size_t data_length_words = data_length / 2;
+    cmd[12] = (data_length_words >> 8) & 0xFF;
+    cmd[13] = data_length_words & 0xFF;
+    // update cmd len
+    cmd_length = 7 + payload_len;
+    // calculate checksum
+    cmd[cmd_length - 2] = checksum(cmd + 1, cmd_length - 3);
+    cmd[cmd_length - 1] = FRAME_END;
+    // send cmd
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, rx_callback, module->buf);
+    furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, cmd_length);
+    uint8_t max_wait = 25;
+    while(!buffer_get_size(module->buf)) {
+        furi_delay_ms(DELAY_MS);
+        if(!max_wait--) break;
+    }
+    uint8_t* buff_data = buffer_get_data(module->buf);
+    size_t buff_length = buffer_get_size(module->buf);
+    if(buff_data[2] == 0xFF && buff_length == 8)
+        return M100NoTagResponse;
+    else if(buff_data[2] == 0xFF)
+        return M100ValidationFail;
+    return M100Success;
+}
 void m100_set_baudrate(M100Module* module, uint16_t baudrate) {
     size_t length = CMD_SET_COMMUNICATION_BAUD_RATE.length;
     uint8_t cmd[length];
@@ -246,7 +352,6 @@ void m100_set_baudrate(M100Module* module, uint16_t baudrate) {
     uint16_t br_mod = baudrate / 100; // module format
     cmd[6] = 0xFF & br_mod; // pow LSB
     cmd[5] = 0xFF & (br_mod >> 4); // pow MSB
-    // furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
     furi_hal_uart_tx(FuriHalUartIdUSART1, cmd, length);
     furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate);
     module->baudrate = baudrate;

+ 38 - 19
uhf_module.h

@@ -10,29 +10,36 @@
 #define FRAME_END 0x7E
 #define DEFAULT_BAUDRATE 115200
 
-typedef struct{
+typedef struct {
     char* hw_version;
     char* sw_version;
     char* manufacturer;
-}M100ModuleInfo;
+} M100ModuleInfo;
 
-typedef enum{
-    WA_CHINA_900 = 1,  // Freq_CH-920.125M
-    WA_US,             // Freq_CH-902.25M
-    WA_EU,             // Freq_CH-865.1M
-    WA_CHINA_800,      // Freq_CH-840.125M
-    WA_KOREA = 6       // Freq_CH-917.1M
+typedef enum {
+    WA_CHINA_900 = 1,   // Freq_CH-920.125M
+    WA_US,              // Freq_CH-902.25M
+    WA_EU,              // Freq_CH-865.1M
+    WA_CHINA_800,       // Freq_CH-840.125M
+    WA_KOREA = 6        // Freq_CH-917.1M
 } WorkingArea;
 
-typedef enum{
-    WC_CHINA_900 = 1,  // CH_Index(CN,900MHz) = (Freq_CH-920.125M)/0.25M
-    WC_US,             // CH_Index(US) = (Freq_CH-902.25M)/0.5M
-    WC_EU,             // CH_Index(EU) = (Freq_CH-865.1M)/0.2M
-    WC_CHINA_800,      // CH_Index(CN,800MHz) = (Freq_CH-840.125M)/0.25M
-    WC_KOREA = 6       // CH_Index(Korea) = (Freq_CH-917.1M)/0.2M
+typedef enum {
+    WC_CHINA_900 = 1,   // CH_Index(CN,900MHz) = (Freq_CH-920.125M)/0.25M
+    WC_US,              // CH_Index(US) = (Freq_CH-902.25M)/0.5M
+    WC_EU,              // CH_Index(EU) = (Freq_CH-865.1M)/0.2M
+    WC_CHINA_800,       // CH_Index(CN,800MHz) = (Freq_CH-840.125M)/0.25M
+    WC_KOREA = 6        // CH_Index(Korea) = (Freq_CH-917.1M)/0.2M
 } WorkingChannel;
 
-typedef struct{
+typedef enum {
+    M100Success,
+    M100ValidationFail,
+    M100NoTagResponse,
+    M100MemoryOverrun
+} M100ResponseType;
+
+typedef struct {
     M100ModuleInfo* info;
     uint16_t baudrate;
     WorkingArea area;
@@ -55,7 +62,6 @@ char* m100_get_hardware_version(M100Module* module);
 char* m100_get_software_version(M100Module* module);
 char* m100_get_manufacturers(M100Module* module);
 
-// set attrs
 void m100_set_baudrate(M100Module* module, uint16_t baudrate);
 bool m100_set_working_area(M100Module* module, WorkingArea area);
 bool m100_set_working_channel(M100Module* module, WorkingChannel channel);
@@ -63,6 +69,19 @@ bool m100_set_transmitting_power(M100Module* module, uint16_t power);
 bool m100_set_freq_hopping(M100Module* module, bool hopping);
 
 // gen2 cmds
-UHFTag* m100_send_single_poll(M100Module* module);
-bool m100_set_select(M100Module* module, UHFTag* uhf_tag);
-bool m100_read_label_data_storage(M100Module* module, UHFTag* uhf_tag, BankType bank, uint32_t access_pwd);
+M100ResponseType m100_send_single_poll(M100Module* module, UHFTag* uhf_tag);
+M100ResponseType m100_set_select(M100Module* module, UHFTag* uhf_tag);
+M100ResponseType m100_read_label_data_storage(
+    M100Module* module,
+    UHFTag* uhf_tag,
+    BankType bank,
+    uint32_t access_pwd,
+    uint16_t word_count);
+
+M100ResponseType m100_write_label_data_storage(
+    M100Module* module,
+    UHFTag* saved_tag,
+    UHFTag* selected_tag,
+    BankType bank,
+    uint16_t source_address,
+    uint32_t access_pwd);

+ 59 - 0
uhf_tag.c

@@ -9,6 +9,9 @@ UHFTagWrapper* uhf_tag_wrapper_alloc() {
 }
 
 void uhf_tag_wrapper_set_tag(UHFTagWrapper* uhf_tag_wrapper, UHFTag* uhf_tag) {
+    if(uhf_tag_wrapper->uhf_tag != NULL) {
+        uhf_tag_free(uhf_tag_wrapper->uhf_tag);
+    }
     uhf_tag_wrapper->uhf_tag = uhf_tag;
 }
 
@@ -54,4 +57,60 @@ void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc) {
 void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size) {
     memcpy(uhf_tag->epc->data, data_in, size);
     uhf_tag->epc->size = size;
+}
+
+void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size) {
+    uhf_tag->epc->size = size;
+}
+
+void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size) {
+    memcpy(uhf_tag->tid->data, data_in, size);
+    uhf_tag->tid->size = size;
+}
+
+void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size) {
+    uhf_tag->tid->size = size;
+}
+
+void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size) {
+    memcpy(uhf_tag->user->data, data_in, size);
+    uhf_tag->user->size = size;
+}
+
+void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size) {
+    uhf_tag->user->size = size;
+}
+
+// getters
+
+uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag) {
+    return uhf_tag->epc->data;
+}
+
+size_t uhf_tag_get_epc_size(UHFTag* uhf_tag) {
+    return uhf_tag->epc->size;
+}
+
+uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag) {
+    return uhf_tag->epc->pc;
+}
+
+uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag) {
+    return uhf_tag->epc->crc;
+}
+
+uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag) {
+    return uhf_tag->tid->data;
+}
+
+size_t uhf_tag_get_tid_size(UHFTag* uhf_tag) {
+    return uhf_tag->tid->size;
+}
+
+uint8_t* uhf_tag_get_user(UHFTag* uhf_tag) {
+    return uhf_tag->user->data;
+}
+
+size_t uhf_tag_get_user_size(UHFTag* uhf_tag) {
+    return uhf_tag->user->size;
 }

+ 5 - 0
uhf_tag.h

@@ -58,12 +58,17 @@ void uhf_tag_set_access_pwd(UHFTag* uhf_tag, uint8_t* data_in);
 void uhf_tag_set_epc_pc(UHFTag* uhf_tag, uint16_t pc);
 void uhf_tag_set_epc_crc(UHFTag* uhf_tag, uint16_t crc);
 void uhf_tag_set_epc(UHFTag* uhf_tag, uint8_t* data_in, size_t size);
+void uhf_tag_set_epc_size(UHFTag* uhf_tag, size_t size);
 void uhf_tag_set_tid(UHFTag* uhf_tag, uint8_t* data_in, size_t size);
+void uhf_tag_set_tid_size(UHFTag* uhf_tag, size_t size);
 void uhf_tag_set_user(UHFTag* uhf_tag, uint8_t* data_in, size_t size);
+void uhf_tag_set_user_size(UHFTag* uhf_tag, size_t size);
 
 uint8_t* uhf_tag_get_kill_pwd(UHFTag* uhf_tag);
 uint8_t* uhf_tag_get_access_pwd(UHFTag* uhf_tag);
 uint8_t* uhf_tag_get_epc(UHFTag* uhf_tag);
+uint16_t uhf_tag_get_epc_pc(UHFTag* uhf_tag);
+uint16_t uhf_tag_get_epc_crc(UHFTag* uhf_tag);
 size_t uhf_tag_get_epc_size(UHFTag* uhf_tag);
 uint8_t* uhf_tag_get_tid(UHFTag* uhf_tag);
 size_t uhf_tag_get_tid_size(UHFTag* uhf_tag);

+ 55 - 164
uhf_worker.c

@@ -11,185 +11,77 @@ UHFWorkerEvent verify_module_connected(UHFWorker* uhf_worker) {
     return UHFWorkerEventSuccess;
 }
 
-uint8_t get_epc_length_in_bits(uint8_t pc) {
-    uint8_t epc_length = pc;
-    epc_length >>= 3;
-    return (uint8_t)epc_length * 16; // x-words * 16 bits
-}
-
-// bool read_bank(UHFData* read_bank_cmd, UHFData* response_bank, UHFBank bank) {
-//     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, response_bank);
-//     read_bank_cmd->data[9] = bank;
-//     read_bank_cmd->data[read_bank_cmd->length - 2] = uhf_data_calculate_checksum(read_bank_cmd);
-//     uhf_data_reset(response_bank);
-//     furi_hal_uart_tx(FuriHalUartIdUSART1, read_bank_cmd->data, read_bank_cmd->length);
-//     furi_delay_ms(CB_DELAY);
-//     return response_bank->data[2] == read_bank_cmd->data[2];
-// }
-
-// bool write_bank(UHFData* write_bank_cmd, UHFBank bank, uint8_t* bank_data, size_t bank_len) {
-//     UHFData* rp_data = uhf_data_alloc();
-//     write_bank_cmd->end = false;
-//     for(size_t i = 0; i < write_bank_cmd->length; i++) {
-//         continue;
-//     }
-//     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, module_rx_callback, rp_data);
-//     for(int i = 5; i < 9; i++) { // no access password for now
-//         write_bank_cmd->data[i] = 0;
-//     }
-//     write_bank_cmd->data[9] = bank;
-//     size_t word_len = bank_len / 2;
-//     write_bank_cmd->data[13] = word_len;
-//     write_bank_cmd->length = 14;
-//     write_bank_cmd->start = true;
-//     for(size_t i = 0; i < bank_len; i++) {
-//         uhf_data_append(write_bank_cmd, bank_data[i]);
-//     }
-//     uhf_data_append(write_bank_cmd, 00);
-//     uhf_data_append(write_bank_cmd, FRAME_END);
-//     write_bank_cmd->data[4] = write_bank_cmd->length - 7;
-//     write_bank_cmd->data[write_bank_cmd->length - 2] = uhf_data_calculate_checksum(write_bank_cmd);
-//     furi_hal_uart_tx(FuriHalUartIdUSART1, write_bank_cmd->data, write_bank_cmd->length);
-//     furi_delay_ms(CB_DELAY);
-//     bool success = rp_data->data[2] == write_bank_cmd->data[2];
-//     uhf_data_free(rp_data);
-//     return success;
-// }
-
 UHFTag* send_polling_command(UHFWorker* uhf_worker) {
     // read epc bank
-    UHFTag* uhf_tag;
+    UHFTag* uhf_tag = uhf_tag_alloc();
     while(true) {
-        uhf_tag = m100_send_single_poll(uhf_worker->module);
+        M100ResponseType status = m100_send_single_poll(uhf_worker->module, uhf_tag);
         furi_delay_ms(100);
         if(uhf_worker->state == UHFWorkerStateStop) {
+            uhf_tag_free(uhf_tag);
             return NULL;
         }
-        if(uhf_tag != NULL) break;
-        FURI_LOG_E("WKR", "null still");
+        if(status == M100Success) break;
     }
     return uhf_tag;
 }
 
+static UHFWorkerEvent
+    read_bank_till_max_length(UHFWorker* uhf_worker, UHFTag* uhf_tag, BankType bank) {
+    unsigned int retry = 3, word_low = 5, word_high = 100;
+    unsigned int word_size;
+    M100ResponseType status;
+    do {
+        if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted;
+        if(word_low >= word_high) return UHFWorkerEventSuccess;
+        word_size = (word_low + word_high) / 2;
+        status = m100_read_label_data_storage(uhf_worker->module, uhf_tag, bank, 0, word_size);
+        if(status == M100Success) {
+            word_low = word_size + 1;
+        } else if(status == M100MemoryOverrun) {
+            word_high = word_size - 1;
+        } else if(status == M100NoTagResponse) {
+            retry--;
+        }
+    } while(retry);
+    return UHFWorkerEventSuccess;
+}
+
 UHFWorkerEvent read_single_card(UHFWorker* uhf_worker) {
     UHFTag* uhf_tag = send_polling_command(uhf_worker);
     if(uhf_tag == NULL) return UHFWorkerEventAborted;
     uhf_tag_wrapper_set_tag(uhf_worker->uhf_tag_wrapper, uhf_tag);
     // Todo : set select here
-    bool select_success = m100_set_select(uhf_worker->module, uhf_tag);
-    FURI_LOG_E("TAG", "select success = %d", select_success);
-
-    // Todo : read rfu
-    m100_read_label_data_storage(uhf_worker->module, uhf_tag, ReservedBank, 0);
-    // Todo : read epc
-    m100_read_label_data_storage(uhf_worker->module, uhf_tag, EPCBank, 0);
-    // Todo : read tid
-    m100_read_label_data_storage(uhf_worker->module, uhf_tag, TIDBank, 0);
-    // Todo : read user
-    m100_read_label_data_storage(uhf_worker->module, uhf_tag, UserBank, 0);
-    // add to tag object
-    // UHFData* raw_bank_data = uhf_data_alloc();
-    // size_t epc_length = (size_t)get_epc_length_in_bits(raw_read_data->data[6]) / 8;
-    // size_t offset = (size_t)(8 + epc_length);
-
-    // UHFData* read_bank_cmd = uhf_data_alloc();
-    // read_bank_cmd->length = CMD_READ_LABEL_DATA_STORAGE.length;
-    // memcpy(
-    //     (void*)&read_bank_cmd->data[0],
-    //     (void*)&CMD_READ_LABEL_DATA_STORAGE.cmd[0],
-    //     read_bank_cmd->length);
-
-    // if(!send_set_select_command(raw_read_data, EPC_BANK)) return UHFWorkerEventFail;
-
-    // int retry = 3;
-    // do {
-    //     if(read_bank(read_bank_cmd, raw_bank_data, EPC_BANK)) {
-    //         uhf_tag_set_epc(uhf_tag, raw_bank_data->data + offset, epc_length + 2);
-    //         break;
-    //     }
-
-    // } while(retry--);
-    // // // debug
-    // // furi_string_reset(temp_str);
-    // // for(size_t i = 0; i < raw_bank_data->length; i++) {
-    // //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
-    // // }
-    // // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
-    // // // e-debug
-    // uhf_data_reset(raw_bank_data);
-    // retry = 3;
-    // do {
-    //     if(read_bank(read_bank_cmd, raw_bank_data, TID_BANK)) {
-    //         uhf_tag_set_tid(uhf_tag, raw_bank_data->data + offset, 16);
-    //         break;
-    //     }
-    // } while(retry--);
-    // // // debug
-    // // furi_string_reset(temp_str);
-    // // for(size_t i = 0; i < raw_bank_data->length; i++) {
-    // //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
-    // // }
-    // // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
-    // // // e-debug
-    // uhf_data_reset(raw_bank_data);
-    // retry = 3;
-    // if(raw_read_data->data[6] & 0x04) {
-    //     do {
-    //         if(read_bank(read_bank_cmd, raw_bank_data, USER_BANK)) {
-    //             uhf_tag_set_user(uhf_tag, raw_bank_data->data + offset, 16);
-    //             break;
-    //         }
-    //     } while(retry--);
-    // }
-    // // // debug
-    // // furi_string_reset(temp_str);
-    // // for(size_t i = 0; i < raw_bank_data->length; i++) {
-    // //     furi_string_cat_printf(temp_str, "%02x ", raw_bank_data->data[i]);
-    // // }
-    // // FURI_LOG_E("TAG", "data = %s", furi_string_get_cstr(temp_str));
-    // // // e-debug
-    // uhf_data_reset(raw_bank_data);
-    // uhf_data_free(raw_bank_data);
-    // uhf_data_free(read_bank_cmd);
-    // // debug
-    // // furi_string_free(temp_str);
-    // // e-debug
-
+    if(m100_set_select(uhf_worker->module, uhf_tag) != M100Success) return UHFWorkerEventFail;
+    // read tid
+    UHFWorkerEvent event;
+    event = read_bank_till_max_length(uhf_worker, uhf_tag, TIDBank);
+    if(event != UHFWorkerEventSuccess) return event;
+    // read user
+    event = read_bank_till_max_length(uhf_worker, uhf_tag, UserBank);
+    if(event != UHFWorkerEventSuccess) return event;
     return UHFWorkerEventSuccess;
 }
 
-// UHFWorkerEvent write_single_card(UHFWorker* uhf_worker) {
-//     UHFResponseData* uhf_response_data = uhf_worker->response_data;
-//     uhf_response_data_reset(uhf_response_data);
-//     UHFData* raw_read_data = uhf_response_data_get_uhf_data(uhf_response_data, 0);
-//     furi_hal_uart_set_br(FuriHalUartIdUSART1, DEFAULT_BAUD_RATE);
-
-//     send_polling_command(uhf_worker, raw_read_data);
-//     // todo : rfu ?
-//     UHFTag* uhf_tag = uhf_worker->uhf_tag;
-
-//     UHFData* write_bank_cmd = uhf_data_alloc();
-//     write_bank_cmd->length = CMD_WRITE_LABEL_DATA_STORAGE.length;
-
-//     memcpy(
-//         (void*)&write_bank_cmd->data[0],
-//         (void*)&CMD_WRITE_LABEL_DATA_STORAGE.cmd[0],
-//         write_bank_cmd->length);
-//     if(!send_set_select_command(raw_read_data, EPC_BANK)) return UHFWorkerEventFail;
-
-//     if(raw_read_data->data[6] & 0x04) {
-//         if(!write_bank(write_bank_cmd, USER_BANK, uhf_tag->user, uhf_tag->user_length))
-//             return UHFWorkerEventFail;
-//     }
-//     uint8_t write_data[uhf_tag->epc_length + 2];
-//     memcpy(&write_data, &raw_read_data->data[raw_read_data->length - 4], 2);
-//     memcpy(&write_data[2], &uhf_tag->epc, uhf_tag->epc_length);
-//     write_data[10] = 0xF1;
-//     if(!write_bank(write_bank_cmd, EPC_BANK, write_data, uhf_tag->epc_length + 2)) {
-//         return UHFWorkerEventFail;
-//     }
-//     return UHFWorkerEventSuccess;
-// }
+UHFWorkerEvent write_single_card(UHFWorker* uhf_worker) {
+    UHFTag* uhf_tag_des = send_polling_command(uhf_worker);
+    if(uhf_tag_des == NULL) return UHFWorkerEventAborted;
+    UHFTag* uhf_tag_from = uhf_worker->uhf_tag_wrapper->uhf_tag;
+    if(m100_set_select(uhf_worker->module, uhf_tag_des) != M100Success) return UHFWorkerEventFail;
+    do {
+        M100ResponseType rp_type = m100_write_label_data_storage(
+            uhf_worker->module, uhf_tag_from, uhf_tag_des, UserBank, 0, 0);
+        if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted;
+        if(rp_type == M100Success) break;
+    } while(true);
+    do {
+        M100ResponseType rp_type = m100_write_label_data_storage(
+            uhf_worker->module, uhf_tag_from, uhf_tag_des, EPCBank, 0, 0);
+        if(uhf_worker->state == UHFWorkerStateStop) return UHFWorkerEventAborted;
+        if(rp_type == M100Success) break;
+    } while(true);
+    return UHFWorkerEventSuccess;
+}
 
 int32_t uhf_worker_task(void* ctx) {
     UHFWorker* uhf_worker = ctx;
@@ -199,11 +91,10 @@ int32_t uhf_worker_task(void* ctx) {
     } else if(uhf_worker->state == UHFWorkerStateDetectSingle) {
         UHFWorkerEvent event = read_single_card(uhf_worker);
         uhf_worker->callback(event, uhf_worker->ctx);
+    } else if(uhf_worker->state == UHFWorkerStateWriteSingle) {
+        UHFWorkerEvent event = write_single_card(uhf_worker);
+        uhf_worker->callback(event, uhf_worker->ctx);
     }
-    // else if(uhf_worker->state == UHFWorkerStateWriteSingle) {
-    //     UHFWorkerEvent event = write_single_card(uhf_worker);
-    //     uhf_worker->callback(event, uhf_worker->ctx);
-    // }
     return 0;
 }