[FL-2393][FL-2381] iButton, OneWire: move to plain C (#1068)
* iButton: getting started on the worker concept * Hal delay: added global instructions_per_us variable * iButton: one wire slave * iButton: ibutton key setter * iButton: one wire host, use ibutton_hal * iButton\RFID: common pulse decoder concept * iButton: cyfral decoder * iButton: worker thread concept * iButton: metakom decoder * iButton: write key through worker * iButton: worker mode holder * iButton: worker improvements * iButton: Cyfral encoder * iButton: Metakom encoder * lib: pulse protocol helpers * iButton: Metakom decoder * iButton: Cyfral decoder * iButton worker: separate modes * iButton: libs documentation * HAL: iButton gpio modes * iButton worker: rename modes file * iButton worker, hal: move to LL * iButton CLI: worker for reading and emulation commands * iButton HAL: correct init and emulation sequence * iButton cli: moved to plain C * iButton: move to worker, small step to plain C * Libs, one wire: move to plain C * Libs: added forgotten files to compilation * iButton writer: get rid of manual disable/enable irq
This commit is contained in:
		
							parent
							
								
									d15a9500c6
								
							
						
					
					
						commit
						bdba15b366
					
				| @ -9,7 +9,7 @@ void AccessorApp::run(void) { | |||||||
|     bool exit = false; |     bool exit = false; | ||||||
| 
 | 
 | ||||||
|     wiegand.begin(); |     wiegand.begin(); | ||||||
|     onewire_master.start(); |     onewire_host_start(onewire_host); | ||||||
| 
 | 
 | ||||||
|     scenes[current_scene]->on_enter(this); |     scenes[current_scene]->on_enter(this); | ||||||
| 
 | 
 | ||||||
| @ -28,19 +28,20 @@ void AccessorApp::run(void) { | |||||||
|     scenes[current_scene]->on_exit(this); |     scenes[current_scene]->on_exit(this); | ||||||
| 
 | 
 | ||||||
|     wiegand.end(); |     wiegand.end(); | ||||||
|     onewire_master.stop(); |     onewire_host_stop(onewire_host); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AccessorApp::AccessorApp() | AccessorApp::AccessorApp() { | ||||||
|     : onewire_master{&ibutton_gpio} { |  | ||||||
|     furi_hal_power_insomnia_enter(); |     furi_hal_power_insomnia_enter(); | ||||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); |     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||||
|  |     onewire_host = onewire_host_alloc(); | ||||||
|     furi_hal_power_enable_otg(); |     furi_hal_power_enable_otg(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| AccessorApp::~AccessorApp() { | AccessorApp::~AccessorApp() { | ||||||
|     furi_hal_power_disable_otg(); |     furi_hal_power_disable_otg(); | ||||||
|     furi_record_close("notification"); |     furi_record_close("notification"); | ||||||
|  |     onewire_host_free(onewire_host); | ||||||
|     furi_hal_power_insomnia_exit(); |     furi_hal_power_insomnia_exit(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -136,6 +137,6 @@ WIEGAND* AccessorApp::get_wiegand() { | |||||||
|     return &wiegand; |     return &wiegand; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| OneWireMaster* AccessorApp::get_one_wire() { | OneWireHost* AccessorApp::get_one_wire() { | ||||||
|     return &onewire_master; |     return onewire_host; | ||||||
| } | } | ||||||
| @ -2,13 +2,9 @@ | |||||||
| #include <map> | #include <map> | ||||||
| #include <list> | #include <list> | ||||||
| #include "accessor_view_manager.h" | #include "accessor_view_manager.h" | ||||||
| 
 |  | ||||||
| #include "scene/accessor_scene_start.h" | #include "scene/accessor_scene_start.h" | ||||||
| 
 |  | ||||||
| #include "helpers/wiegand.h" | #include "helpers/wiegand.h" | ||||||
| 
 | #include <one_wire/one_wire_host.h> | ||||||
| #include <one_wire_master.h> |  | ||||||
| 
 |  | ||||||
| #include <notification/notification_messages.h> | #include <notification/notification_messages.h> | ||||||
| 
 | 
 | ||||||
| class AccessorApp { | class AccessorApp { | ||||||
| @ -37,7 +33,7 @@ public: | |||||||
|     void set_text_store(const char* text...); |     void set_text_store(const char* text...); | ||||||
| 
 | 
 | ||||||
|     WIEGAND* get_wiegand(); |     WIEGAND* get_wiegand(); | ||||||
|     OneWireMaster* get_one_wire(); |     OneWireHost* get_one_wire(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::list<Scene> previous_scenes_list = {Scene::Exit}; |     std::list<Scene> previous_scenes_list = {Scene::Exit}; | ||||||
| @ -52,7 +48,7 @@ private: | |||||||
|     char text_store[text_store_size + 1]; |     char text_store[text_store_size + 1]; | ||||||
| 
 | 
 | ||||||
|     WIEGAND wiegand; |     WIEGAND wiegand; | ||||||
|     OneWireMaster onewire_master; |     OneWireHost* onewire_host; | ||||||
| 
 | 
 | ||||||
|     NotificationApp* notification; |     NotificationApp* notification; | ||||||
| }; | }; | ||||||
| @ -21,7 +21,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|     if(event->type == AccessorEvent::Type::Tick) { |     if(event->type == AccessorEvent::Type::Tick) { | ||||||
|         WIEGAND* wiegand = app->get_wiegand(); |         WIEGAND* wiegand = app->get_wiegand(); | ||||||
|         Popup* popup = app->get_view_manager()->get_popup(); |         Popup* popup = app->get_view_manager()->get_popup(); | ||||||
|         OneWireMaster* onewire = app->get_one_wire(); |         OneWireHost* onewire_host = app->get_one_wire(); | ||||||
| 
 | 
 | ||||||
|         uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |         uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||||
|         uint8_t type = 0; |         uint8_t type = 0; | ||||||
| @ -38,11 +38,11 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             FURI_CRITICAL_ENTER(); |             FURI_CRITICAL_ENTER(); | ||||||
|             if(onewire->reset()) { |             if(onewire_host_reset(onewire_host)) { | ||||||
|                 type = 255; |                 type = 255; | ||||||
|                 onewire->write(0x33); |                 onewire_host_write(onewire_host, 0x33); | ||||||
|                 for(uint8_t i = 0; i < 8; i++) { |                 for(uint8_t i = 0; i < 8; i++) { | ||||||
|                     data[i] = onewire->read(); |                     data[i] = onewire_host_read(onewire_host); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 for(uint8_t i = 0; i < 7; i++) { |                 for(uint8_t i = 0; i < 7; i++) { | ||||||
|  | |||||||
| @ -1,193 +0,0 @@ | |||||||
| #include "cyfral_decoder.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| void CyfralDecoder::reset_state() { |  | ||||||
|     state = State::WAIT_START_NIBBLE; |  | ||||||
|     bit_state = BitState::WAIT_FRONT_LOW; |  | ||||||
| 
 |  | ||||||
|     period_time = 0; |  | ||||||
|     bit_index = 0; |  | ||||||
|     ready = false; |  | ||||||
|     index = 0; |  | ||||||
| 
 |  | ||||||
|     key_data = 0; |  | ||||||
|     readed_nibble = 0; |  | ||||||
|     data_valid = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralDecoder::nibble_valid(uint8_t data) { |  | ||||||
|     uint8_t data_value = data & 0x0F; |  | ||||||
| 
 |  | ||||||
|     switch(data_value) { |  | ||||||
|     case 0b1110: |  | ||||||
|     case 0b1101: |  | ||||||
|     case 0b1011: |  | ||||||
|     case 0b0111: |  | ||||||
|         return true; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralDecoder::CyfralDecoder() { |  | ||||||
|     reset_state(); |  | ||||||
|     max_period = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralDecoder::process_front(bool polarity, uint32_t time) { |  | ||||||
|     bool readed; |  | ||||||
|     bool value; |  | ||||||
| 
 |  | ||||||
|     if(max_period == 0) { |  | ||||||
|         max_period = 230 * (SystemCoreClock / 1000000.0f); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(ready) return; |  | ||||||
| 
 |  | ||||||
|     switch(state) { |  | ||||||
|     case State::WAIT_START_NIBBLE: |  | ||||||
|         // wait for start word
 |  | ||||||
|         if(process_bit(polarity, time, &readed, &value)) { |  | ||||||
|             if(readed) { |  | ||||||
|                 readed_nibble = ((readed_nibble << 1) | value) & 0x0F; |  | ||||||
|                 if(readed_nibble == 0b0001) { |  | ||||||
|                     readed_nibble = 0; |  | ||||||
|                     state = State::READ_NIBBLE; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             reset_state(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         break; |  | ||||||
|     case State::READ_NIBBLE: |  | ||||||
|         // read nibbles
 |  | ||||||
|         if(process_bit(polarity, time, &readed, &value)) { |  | ||||||
|             if(readed) { |  | ||||||
|                 readed_nibble = (readed_nibble << 1) | value; |  | ||||||
| 
 |  | ||||||
|                 bit_index++; |  | ||||||
| 
 |  | ||||||
|                 //convert every nibble to 2-bit index
 |  | ||||||
|                 if(bit_index == 4) { |  | ||||||
|                     switch(readed_nibble) { |  | ||||||
|                     case 0b1110: |  | ||||||
|                         key_data = (key_data << 2) | 0b11; |  | ||||||
|                         break; |  | ||||||
|                     case 0b1101: |  | ||||||
|                         key_data = (key_data << 2) | 0b10; |  | ||||||
|                         break; |  | ||||||
|                     case 0b1011: |  | ||||||
|                         key_data = (key_data << 2) | 0b01; |  | ||||||
|                         break; |  | ||||||
|                     case 0b0111: |  | ||||||
|                         key_data = (key_data << 2) | 0b00; |  | ||||||
|                         break; |  | ||||||
|                     default: |  | ||||||
|                         data_valid = false; |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     readed_nibble = 0; |  | ||||||
|                     bit_index = 0; |  | ||||||
|                     index++; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // succefully read 8 nibbles
 |  | ||||||
|                 if(index == 8) { |  | ||||||
|                     state = State::READ_STOP_NIBBLE; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             reset_state(); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case State::READ_STOP_NIBBLE: |  | ||||||
|         // read stop nibble
 |  | ||||||
|         if(process_bit(polarity, time, &readed, &value)) { |  | ||||||
|             if(readed) { |  | ||||||
|                 readed_nibble = ((readed_nibble << 1) | value) & 0x0F; |  | ||||||
|                 bit_index++; |  | ||||||
| 
 |  | ||||||
|                 switch(bit_index) { |  | ||||||
|                 case 0: |  | ||||||
|                 case 1: |  | ||||||
|                 case 2: |  | ||||||
|                 case 3: |  | ||||||
|                     break; |  | ||||||
|                 case 4: |  | ||||||
|                     if(readed_nibble == 0b0001) { |  | ||||||
|                         // validate data
 |  | ||||||
|                         if(data_valid) { |  | ||||||
|                             ready = true; |  | ||||||
|                         } else { |  | ||||||
|                             reset_state(); |  | ||||||
|                         } |  | ||||||
|                     } else { |  | ||||||
|                         reset_state(); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     reset_state(); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             reset_state(); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralDecoder::process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value) { |  | ||||||
|     bool result = true; |  | ||||||
|     *readed = false; |  | ||||||
| 
 |  | ||||||
|     // bit start from low
 |  | ||||||
|     switch(bit_state) { |  | ||||||
|     case BitState::WAIT_FRONT_LOW: |  | ||||||
|         if(polarity == true) { |  | ||||||
|             period_time += time; |  | ||||||
| 
 |  | ||||||
|             *readed = true; |  | ||||||
|             if(period_time <= max_period) { |  | ||||||
|                 if((period_time / 2) > time) { |  | ||||||
|                     *readed_value = false; |  | ||||||
|                 } else { |  | ||||||
|                     *readed_value = true; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 result = false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bit_state = BitState::WAIT_FRONT_HIGH; |  | ||||||
|         } else { |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case BitState::WAIT_FRONT_HIGH: |  | ||||||
|         if(polarity == false) { |  | ||||||
|             period_time = time; |  | ||||||
|             bit_state = BitState::WAIT_FRONT_LOW; |  | ||||||
|         } else { |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralDecoder::read(uint8_t* _data, uint8_t data_size) { |  | ||||||
|     furi_check(data_size <= 2); |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(ready) { |  | ||||||
|         memcpy(_data, &key_data, data_size); |  | ||||||
|         reset_state(); |  | ||||||
|         result = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,55 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| class CyfralDecoder { |  | ||||||
| public: |  | ||||||
|     bool read(uint8_t* data, uint8_t data_size); |  | ||||||
|     void process_front(bool polarity, uint32_t time); |  | ||||||
| 
 |  | ||||||
|     CyfralDecoder(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     enum class BitState : uint8_t { |  | ||||||
|         WAIT_FRONT_HIGH, |  | ||||||
|         WAIT_FRONT_LOW, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     enum class State : uint8_t { |  | ||||||
|         WAIT_START_NIBBLE, |  | ||||||
|         READ_NIBBLE, |  | ||||||
|         READ_STOP_NIBBLE, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     State state; |  | ||||||
|     BitState bit_state; |  | ||||||
| 
 |  | ||||||
|     bool process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value); |  | ||||||
|     void reset_state(); |  | ||||||
|     bool nibble_valid(uint8_t data); |  | ||||||
| 
 |  | ||||||
|     // high + low period time
 |  | ||||||
|     uint32_t period_time; |  | ||||||
| 
 |  | ||||||
|     // ready flag, key is readed and valid
 |  | ||||||
|     std::atomic<bool> ready; |  | ||||||
| 
 |  | ||||||
|     // key data storage
 |  | ||||||
|     uint16_t key_data; |  | ||||||
| 
 |  | ||||||
|     // temporary nibble storage
 |  | ||||||
|     uint8_t readed_nibble; |  | ||||||
| 
 |  | ||||||
|     // data valid flag
 |  | ||||||
|     // MUST be checked only in READ_STOP_NIBBLE state
 |  | ||||||
|     bool data_valid; |  | ||||||
| 
 |  | ||||||
|     // nibble index, we expect 8 nibbles
 |  | ||||||
|     uint8_t index; |  | ||||||
| 
 |  | ||||||
|     // bit index in nibble, 4 bit per nibble
 |  | ||||||
|     uint8_t bit_index; |  | ||||||
| 
 |  | ||||||
|     // max period, 230us x clock per us
 |  | ||||||
|     uint32_t max_period; |  | ||||||
| }; |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| class RW1990_1 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1; |  | ||||||
|     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class RW1990_2 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D; |  | ||||||
|     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TM2004 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_READ_STATUS = 0xAA; |  | ||||||
|     constexpr static const uint8_t CMD_READ_MEMORY = 0xF0; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0x3C; |  | ||||||
|     constexpr static const uint8_t CMD_FINALIZATION = 0x35; |  | ||||||
| 
 |  | ||||||
|     constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TM01 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xC5; |  | ||||||
|     constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA; |  | ||||||
|     constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class DS1990 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_READ_ROM = 0x33; |  | ||||||
| }; |  | ||||||
| @ -1,208 +0,0 @@ | |||||||
| #include "key_emulator.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| 
 |  | ||||||
| KeyEmulator::~KeyEmulator() { |  | ||||||
|     stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyEmulator::KeyEmulator(OneWireSlave* _onewire_slave) |  | ||||||
|     : dallas_key{0, 0, 0, 0, 0, 0, 0} { |  | ||||||
|     onewire_slave = _onewire_slave; |  | ||||||
| 
 |  | ||||||
|     auto cb = cbc::obtain_connector(this, &KeyEmulator::result_callback); |  | ||||||
|     onewire_slave->set_result_callback(cb, this); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::start(iButtonKey* key) { |  | ||||||
|     anything_emulated = false; |  | ||||||
|     stop(); |  | ||||||
| 
 |  | ||||||
|     // pulldown pull pin, to prevent low-pass filtering by the RFID part of the schematic
 |  | ||||||
|     furi_hal_rfid_pin_pull_pulldown(); |  | ||||||
| 
 |  | ||||||
|     switch(key->get_key_type()) { |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         start_dallas_emulate(key); |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyCyfral: |  | ||||||
|         start_cyfral_emulate(key); |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyMetakom: |  | ||||||
|         start_metakom_emulate(key); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyEmulator::emulated() { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(anything_emulated) { |  | ||||||
|         anything_emulated = false; |  | ||||||
|         result = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::stop() { |  | ||||||
|     onewire_slave->stop(); |  | ||||||
|     pulser.stop(); |  | ||||||
|     furi_hal_rfid_pins_reset(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::start_cyfral_emulate(iButtonKey* key) { |  | ||||||
|     furi_assert(key->get_key_type() == iButtonKeyType::KeyCyfral); |  | ||||||
|     furi_assert(key->get_type_data_size() == 2); |  | ||||||
| 
 |  | ||||||
|     const uint32_t cyfral_period_full = 8000; |  | ||||||
|     const uint32_t cyfral_period_one[2] = { |  | ||||||
|         uint32_t(cyfral_period_full * 0.33f), uint32_t(cyfral_period_full * 0.66f)}; |  | ||||||
|     const uint32_t cyfral_period_zero[2] = { |  | ||||||
|         uint32_t(cyfral_period_full * 0.66f), uint32_t(cyfral_period_full * 0.33f)}; |  | ||||||
|     uint8_t pd_index = 0; |  | ||||||
|     uint8_t* key_data = key->get_data(); |  | ||||||
| 
 |  | ||||||
|     // start nibble
 |  | ||||||
|     set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|     pd_index++; |  | ||||||
|     set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|     pd_index++; |  | ||||||
|     set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|     pd_index++; |  | ||||||
|     set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|     pd_index++; |  | ||||||
| 
 |  | ||||||
|     // data nibbles x 8
 |  | ||||||
|     for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) { |  | ||||||
|         for(int8_t j = 3; j >= 0; j--) { |  | ||||||
|             switch((key_data[i] >> (j * 2)) & 0b00000011) { |  | ||||||
|             case 0b11: |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|                 pd_index++; |  | ||||||
|                 break; |  | ||||||
|             case 0b10: |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 break; |  | ||||||
|             case 0b01: |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 break; |  | ||||||
|             case 0b00: |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_zero); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 set_pulse_data_cyfral(pd_index, cyfral_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 // cannot be anyway
 |  | ||||||
|                 furi_check(false); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // 4 (nibbles) x (8 data + 1 start) = 4 x 9 = 36
 |  | ||||||
|     if(pd_index != 36) { |  | ||||||
|         // something is very wrong
 |  | ||||||
|         furi_check(false); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pulser.set_periods(pulse_data, 72, false); |  | ||||||
|     pulser.start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::start_metakom_emulate(iButtonKey* key) { |  | ||||||
|     furi_assert(key->get_key_type() == iButtonKeyType::KeyMetakom); |  | ||||||
|     furi_assert(key->get_type_data_size() == 4); |  | ||||||
| 
 |  | ||||||
|     const uint32_t metakom_period_full = 8000; |  | ||||||
|     const uint32_t metakom_period_zero[2] = { |  | ||||||
|         uint32_t(metakom_period_full * 0.33f), uint32_t(metakom_period_full * 0.66f)}; |  | ||||||
|     const uint32_t metakom_period_one[2] = { |  | ||||||
|         uint32_t(metakom_period_full * 0.66f), uint32_t(metakom_period_full * 0.33f)}; |  | ||||||
|     uint8_t pd_index = 0; |  | ||||||
| 
 |  | ||||||
|     uint8_t* key_data = key->get_data(); |  | ||||||
| 
 |  | ||||||
|     // start pulse
 |  | ||||||
|     pulse_data[0] = metakom_period_full; |  | ||||||
| 
 |  | ||||||
|     // start triplet
 |  | ||||||
|     set_pulse_data_metakom(pd_index, metakom_period_zero); |  | ||||||
|     pd_index++; |  | ||||||
|     set_pulse_data_metakom(pd_index, metakom_period_one); |  | ||||||
|     pd_index++; |  | ||||||
|     set_pulse_data_metakom(pd_index, metakom_period_zero); |  | ||||||
|     pd_index++; |  | ||||||
| 
 |  | ||||||
|     for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) { |  | ||||||
|         for(int8_t j = 7; j >= 0; j--) { |  | ||||||
|             if(((key_data[i] >> j) & 0b00000001) == 1) { |  | ||||||
|                 set_pulse_data_metakom(pd_index, metakom_period_one); |  | ||||||
|                 pd_index++; |  | ||||||
|             } else { |  | ||||||
|                 set_pulse_data_metakom(pd_index, metakom_period_zero); |  | ||||||
|                 pd_index++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // 4 byte x 8 bits + 3 start bits = 35
 |  | ||||||
|     if(pd_index != 35) { |  | ||||||
|         // something is very wrong
 |  | ||||||
|         furi_check(false); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pulser.set_periods(pulse_data, 71, false); |  | ||||||
|     pulser.start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::start_dallas_emulate(iButtonKey* key) { |  | ||||||
|     furi_assert(key->get_key_type() == iButtonKeyType::KeyDallas); |  | ||||||
|     furi_assert(key->get_type_data_size() == 8); |  | ||||||
| 
 |  | ||||||
|     onewire_slave->deattach(); |  | ||||||
|     memcpy(dallas_key.id_storage, key->get_data(), key->get_type_data_size()); |  | ||||||
|     onewire_slave->attach(&dallas_key); |  | ||||||
|     onewire_slave->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::set_pulse_data_cyfral(uint8_t index, const uint32_t* data) { |  | ||||||
|     pulse_data[index * 2] = data[0]; |  | ||||||
|     pulse_data[index * 2 + 1] = data[1]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::set_pulse_data_metakom(uint8_t index, const uint32_t* data) { |  | ||||||
|     // damn start pulse
 |  | ||||||
|     pulse_data[(index * 2) + 1] = data[0]; |  | ||||||
|     pulse_data[(index * 2) + 2] = data[1]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyEmulator::result_callback(bool success, void* ctx) { |  | ||||||
|     KeyEmulator* _this = static_cast<KeyEmulator*>(ctx); |  | ||||||
| 
 |  | ||||||
|     _this->anything_emulated = true; |  | ||||||
| } |  | ||||||
| @ -1,34 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "pulse_sequencer.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <one_wire_slave.h> |  | ||||||
| #include <one_wire_device_ds_1990.h> |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| class KeyEmulator { |  | ||||||
| public: |  | ||||||
|     KeyEmulator(OneWireSlave* onewire_slave); |  | ||||||
|     ~KeyEmulator(); |  | ||||||
| 
 |  | ||||||
|     void start(iButtonKey* key); |  | ||||||
|     bool emulated(); |  | ||||||
|     void stop(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     DS1990 dallas_key; |  | ||||||
|     OneWireSlave* onewire_slave; |  | ||||||
| 
 |  | ||||||
|     PulseSequencer pulser; |  | ||||||
|     uint32_t pulse_data[72]; |  | ||||||
| 
 |  | ||||||
|     std::atomic<bool> anything_emulated; |  | ||||||
| 
 |  | ||||||
|     void start_cyfral_emulate(iButtonKey* key); |  | ||||||
|     void start_metakom_emulate(iButtonKey* key); |  | ||||||
|     void start_dallas_emulate(iButtonKey* key); |  | ||||||
| 
 |  | ||||||
|     void set_pulse_data_cyfral(uint8_t index, const uint32_t* data); |  | ||||||
|     void set_pulse_data_metakom(uint8_t index, const uint32_t* data); |  | ||||||
| 
 |  | ||||||
|     void result_callback(bool success, void* ctx); |  | ||||||
| }; |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| static const uint8_t IBUTTON_KEY_DATA_SIZE = 8; |  | ||||||
| static const uint8_t IBUTTON_KEY_NAME_SIZE = 22; |  | ||||||
| 
 |  | ||||||
| enum class iButtonKeyType : uint8_t { |  | ||||||
|     KeyDallas, |  | ||||||
|     KeyCyfral, |  | ||||||
|     KeyMetakom, |  | ||||||
| }; |  | ||||||
| @ -1,181 +0,0 @@ | |||||||
| #include "key_reader.h" |  | ||||||
| #include "key_commands.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| #include <maxim_crc.h> |  | ||||||
| 
 |  | ||||||
| KeyReader::Error KeyReader::read(iButtonKey* key) { |  | ||||||
|     uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |  | ||||||
|     iButtonKeyType key_type; |  | ||||||
| 
 |  | ||||||
|     KeyReader::Error result = KeyReader::Error::EMPTY; |  | ||||||
| 
 |  | ||||||
|     if(read_key(&key_type, tmp_key_data, 8)) { |  | ||||||
|         switch(key_type) { |  | ||||||
|         case iButtonKeyType::KeyDallas: |  | ||||||
|             if(verify_key(key_type, tmp_key_data, 8)) { |  | ||||||
|                 if(maxim_crc8(tmp_key_data, 8) == 0) { |  | ||||||
|                     if(tmp_key_data[0] == 0x01) { |  | ||||||
|                         result = KeyReader::Error::OK; |  | ||||||
|                     } else { |  | ||||||
|                         result = KeyReader::Error::NOT_ARE_KEY; |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     result = KeyReader::Error::CRC_ERROR; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             break; |  | ||||||
|         case iButtonKeyType::KeyCyfral: |  | ||||||
|             result = KeyReader::Error::OK; |  | ||||||
|             break; |  | ||||||
|         case iButtonKeyType::KeyMetakom: |  | ||||||
|             result = KeyReader::Error::OK; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(result != KeyReader::Error::EMPTY) { |  | ||||||
|             key->set_type(key_type); |  | ||||||
|             key->set_data(tmp_key_data, 8); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     switch_mode_if_needed(); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyReader::KeyReader(OneWireMaster* _onewire_master) { |  | ||||||
|     onewire_master = _onewire_master; |  | ||||||
|     read_mode_switch_time = 0; |  | ||||||
|     read_mode = ReadMode::DALLAS; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyReader::~KeyReader() { |  | ||||||
|     stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { |  | ||||||
|     bool readed = false; |  | ||||||
| 
 |  | ||||||
|     if(read_mode == ReadMode::DALLAS) { |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
|         if(onewire_master->search(data)) { |  | ||||||
|             onewire_master->reset_search(); |  | ||||||
|             readed = true; |  | ||||||
|             *key_type = iButtonKeyType::KeyDallas; |  | ||||||
|         } else { |  | ||||||
|             onewire_master->reset_search(); |  | ||||||
|         } |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
|     } else if(read_mode == ReadMode::CYFRAL_METAKOM) { |  | ||||||
|         if(cyfral_decoder.read(data, 2)) { |  | ||||||
|             readed = true; |  | ||||||
|             *key_type = iButtonKeyType::KeyCyfral; |  | ||||||
|         } else if(metakom_decoder.read(data, 4)) { |  | ||||||
|             readed = true; |  | ||||||
|             *key_type = iButtonKeyType::KeyMetakom; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return readed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     if(key_type == iButtonKeyType::KeyDallas) { |  | ||||||
|         switch_to(ReadMode::DALLAS); |  | ||||||
| 
 |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
|         if(onewire_master->reset()) { |  | ||||||
|             onewire_master->write(DS1990::CMD_READ_ROM); |  | ||||||
|             for(uint8_t i = 0; i < data_size; i++) { |  | ||||||
|                 if(onewire_master->read() != data[i]) { |  | ||||||
|                     result = false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
| 
 |  | ||||||
|     } else { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::start_comaparator(void) { |  | ||||||
|     furi_hal_rfid_pins_reset(); |  | ||||||
| 
 |  | ||||||
|     // pulldown pull pin, we sense the signal through the analog part of the RFID schematic
 |  | ||||||
|     furi_hal_rfid_pin_pull_pulldown(); |  | ||||||
| 
 |  | ||||||
|     comparator_callback_pointer = |  | ||||||
|         cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback); |  | ||||||
|     furi_hal_rfid_comp_set_callback(comparator_callback_pointer, this); |  | ||||||
|     last_dwt_value = DWT->CYCCNT; |  | ||||||
|     furi_hal_rfid_comp_start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::stop_comaparator(void) { |  | ||||||
|     furi_hal_rfid_pins_reset(); |  | ||||||
| 
 |  | ||||||
|     // rfid_pins_reset will disable ibutton pin
 |  | ||||||
|     furi_hal_ibutton_start(); |  | ||||||
| 
 |  | ||||||
|     furi_hal_rfid_comp_stop(); |  | ||||||
|     furi_hal_rfid_comp_set_callback(NULL, NULL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::comparator_trigger_callback(bool level, void* comp_ctx) { |  | ||||||
|     KeyReader* _this = static_cast<KeyReader*>(comp_ctx); |  | ||||||
| 
 |  | ||||||
|     uint32_t current_dwt_value = DWT->CYCCNT; |  | ||||||
| 
 |  | ||||||
|     _this->cyfral_decoder.process_front(level, current_dwt_value - last_dwt_value); |  | ||||||
|     _this->metakom_decoder.process_front(level, current_dwt_value - last_dwt_value); |  | ||||||
| 
 |  | ||||||
|     last_dwt_value = current_dwt_value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::switch_to(ReadMode mode) { |  | ||||||
|     switch(mode) { |  | ||||||
|     case ReadMode::DALLAS: |  | ||||||
|         onewire_master->start(); |  | ||||||
|         stop_comaparator(); |  | ||||||
|         break; |  | ||||||
|     case ReadMode::CYFRAL_METAKOM: |  | ||||||
|         onewire_master->stop(); |  | ||||||
|         start_comaparator(); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     read_mode = mode; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::switch_mode_if_needed() { |  | ||||||
|     if(osKernelGetTickCount() - read_mode_switch_time > (osKernelGetTickFreq() / 5)) { |  | ||||||
|         read_mode_switch_time = osKernelGetTickCount(); |  | ||||||
|         switch(read_mode) { |  | ||||||
|         case ReadMode::DALLAS: |  | ||||||
|             switch_to(ReadMode::CYFRAL_METAKOM); |  | ||||||
|             break; |  | ||||||
|         case ReadMode::CYFRAL_METAKOM: |  | ||||||
|             switch_to(ReadMode::DALLAS); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::start() { |  | ||||||
|     furi_hal_power_enable_otg(); |  | ||||||
|     switch_to(ReadMode::CYFRAL_METAKOM); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyReader::stop() { |  | ||||||
|     furi_hal_power_disable_otg(); |  | ||||||
|     onewire_master->stop(); |  | ||||||
|     stop_comaparator(); |  | ||||||
| } |  | ||||||
| @ -1,54 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include "cyfral_decoder.h" |  | ||||||
| #pragma once |  | ||||||
| #include "metakom_decoder.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <one_wire_master.h> |  | ||||||
| #include <one_wire_slave.h> |  | ||||||
| 
 |  | ||||||
| class KeyReader { |  | ||||||
| public: |  | ||||||
|     enum class Error : uint8_t { |  | ||||||
|         EMPTY, |  | ||||||
|         CRC_ERROR, |  | ||||||
|         NOT_ARE_KEY, |  | ||||||
|         OK, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     void start(); |  | ||||||
|     void stop(); |  | ||||||
|     KeyReader::Error read(iButtonKey* key); |  | ||||||
|     KeyReader(OneWireMaster* onewire_master); |  | ||||||
|     ~KeyReader(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     bool read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size); |  | ||||||
|     bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size); |  | ||||||
| 
 |  | ||||||
|     // cyfral and metakom readers data
 |  | ||||||
|     void comparator_trigger_callback(bool level, void* comp_ctx); |  | ||||||
|     void (*comparator_callback_pointer)(bool level, void* comp_ctx); |  | ||||||
| 
 |  | ||||||
|     void start_comaparator(void); |  | ||||||
|     void stop_comaparator(void); |  | ||||||
|     uint32_t last_dwt_value; |  | ||||||
| 
 |  | ||||||
|     CyfralDecoder cyfral_decoder; |  | ||||||
|     MetakomDecoder metakom_decoder; |  | ||||||
| 
 |  | ||||||
|     // mode
 |  | ||||||
|     uint32_t read_mode_switch_time; |  | ||||||
|     enum class ReadMode : uint8_t { |  | ||||||
|         CYFRAL_METAKOM, |  | ||||||
|         DALLAS, |  | ||||||
|     }; |  | ||||||
|     ReadMode read_mode; |  | ||||||
| 
 |  | ||||||
|     // one wire
 |  | ||||||
|     OneWireMaster* onewire_master; |  | ||||||
| 
 |  | ||||||
|     void switch_to(ReadMode mode); |  | ||||||
|     void switch_mode_if_needed(); |  | ||||||
| }; |  | ||||||
| @ -1,52 +0,0 @@ | |||||||
| #include "key_worker.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| #include <maxim_crc.h> |  | ||||||
| 
 |  | ||||||
| KeyReader::Error KeyWorker::read(iButtonKey* key) { |  | ||||||
|     KeyReader::Error result = key_reader.read(key); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::start_read() { |  | ||||||
|     key_reader.start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::stop_read() { |  | ||||||
|     key_reader.stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWorker::emulated() { |  | ||||||
|     return key_emulator.emulated(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::start_emulate(iButtonKey* key) { |  | ||||||
|     key_emulator.start(key); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::stop_emulate() { |  | ||||||
|     key_emulator.stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWriter::Error KeyWorker::write(iButtonKey* key) { |  | ||||||
|     return key_writer.write(key); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::start_write() { |  | ||||||
|     key_writer.start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWorker::stop_write() { |  | ||||||
|     key_writer.stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWorker::KeyWorker(const GpioPin* one_wire_gpio) |  | ||||||
|     : onewire_master{one_wire_gpio} |  | ||||||
|     , onewire_slave{one_wire_gpio} |  | ||||||
|     , key_reader{&onewire_master} |  | ||||||
|     , key_emulator{&onewire_slave} |  | ||||||
|     , key_writer{&onewire_master} { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWorker::~KeyWorker() { |  | ||||||
| } |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <furi.h> |  | ||||||
| #include "key_info.h" |  | ||||||
| #include "key_reader.h" |  | ||||||
| #include "key_emulator.h" |  | ||||||
| #include "key_writer.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <one_wire_master.h> |  | ||||||
| #include <one_wire_slave.h> |  | ||||||
| 
 |  | ||||||
| class KeyWorker { |  | ||||||
| public: |  | ||||||
|     KeyReader::Error read(iButtonKey* key); |  | ||||||
|     void start_read(); |  | ||||||
|     void stop_read(); |  | ||||||
| 
 |  | ||||||
|     bool emulated(); |  | ||||||
|     void start_emulate(iButtonKey* key); |  | ||||||
|     void stop_emulate(); |  | ||||||
| 
 |  | ||||||
|     KeyWriter::Error write(iButtonKey* key); |  | ||||||
|     void start_write(); |  | ||||||
|     void stop_write(); |  | ||||||
| 
 |  | ||||||
|     KeyWorker(const GpioPin* one_wire_gpio); |  | ||||||
|     ~KeyWorker(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     // one wire
 |  | ||||||
|     OneWireMaster onewire_master; |  | ||||||
|     OneWireSlave onewire_slave; |  | ||||||
|     KeyReader key_reader; |  | ||||||
|     KeyEmulator key_emulator; |  | ||||||
|     KeyWriter key_writer; |  | ||||||
| }; |  | ||||||
| @ -1,278 +0,0 @@ | |||||||
| #include "key_writer.h" |  | ||||||
| #include "key_commands.h" |  | ||||||
| 
 |  | ||||||
| KeyWriter::KeyWriter(OneWireMaster* _onewire_master) { |  | ||||||
|     onewire_master = _onewire_master; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWriter::~KeyWriter() { |  | ||||||
|     stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWriter::Error KeyWriter::write(iButtonKey* key) { |  | ||||||
|     return write_internal(key); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWriter::start() { |  | ||||||
|     furi_hal_power_enable_otg(); |  | ||||||
|     onewire_master->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWriter::stop() { |  | ||||||
|     furi_hal_power_disable_otg(); |  | ||||||
|     onewire_master->stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWriter::Error KeyWriter::write_internal(iButtonKey* key) { |  | ||||||
|     Error result = Error::NO_DETECT; |  | ||||||
|     bool same_key = false; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     bool presence = onewire_master->reset(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(presence) { |  | ||||||
|         switch(key->get_key_type()) { |  | ||||||
|         case iButtonKeyType::KeyDallas: |  | ||||||
|             same_key = compare_key_ds1990(key); |  | ||||||
| 
 |  | ||||||
|             if(!same_key) { |  | ||||||
|                 bool write_result = false; |  | ||||||
|                 // currently we can write:
 |  | ||||||
|                 // RW1990, TM08v2, TM08vi-2 by write_1990_1()
 |  | ||||||
|                 // RW2004, RW2004 with EEPROM by write_TM2004();
 |  | ||||||
| 
 |  | ||||||
|                 if(!write_result) { |  | ||||||
|                     write_result = write_1990_1(key); |  | ||||||
|                 } |  | ||||||
|                 if(!write_result) { |  | ||||||
|                     write_result = write_1990_2(key); |  | ||||||
|                 } |  | ||||||
|                 if(!write_result) { |  | ||||||
|                     write_result = write_TM2004(key); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(write_result) { |  | ||||||
|                     result = Error::OK; |  | ||||||
|                 } else { |  | ||||||
|                     result = Error::CANNOT_WRITE; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 result = Error::SAME_KEY; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWriter::compare_key_ds1990(iButtonKey* key) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
|         bool presence = onewire_master->reset(); |  | ||||||
| 
 |  | ||||||
|         if(presence) { |  | ||||||
|             onewire_master->write(DS1990::CMD_READ_ROM); |  | ||||||
| 
 |  | ||||||
|             result = true; |  | ||||||
|             for(uint8_t i = 0; i < key->get_type_data_size(); i++) { |  | ||||||
|                 if(key->get_data()[i] != onewire_master->read()) { |  | ||||||
|                     result = false; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWriter::write_1990_1(iButtonKey* key) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
| 
 |  | ||||||
|         // unlock
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |  | ||||||
|         delay_us(10); |  | ||||||
|         onewire_write_one_bit(0, 5000); |  | ||||||
| 
 |  | ||||||
|         // write key
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         onewire_master->write(RW1990_1::CMD_WRITE_ROM); |  | ||||||
|         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { |  | ||||||
|             // inverted key for RW1990.1
 |  | ||||||
|             write_byte_ds1990(~key->get_data()[i]); |  | ||||||
|             delay_us(30000); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // lock
 |  | ||||||
|         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |  | ||||||
|         onewire_write_one_bit(1); |  | ||||||
| 
 |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
| 
 |  | ||||||
|         if(compare_key_ds1990(key)) { |  | ||||||
|             result = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWriter::write_1990_2(iButtonKey* key) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
| 
 |  | ||||||
|         // unlock
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |  | ||||||
|         delay_us(10); |  | ||||||
|         onewire_write_one_bit(1, 5000); |  | ||||||
| 
 |  | ||||||
|         // write key
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         onewire_master->write(RW1990_2::CMD_WRITE_ROM); |  | ||||||
|         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { |  | ||||||
|             write_byte_ds1990(key->get_data()[i]); |  | ||||||
|             delay_us(30000); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // lock
 |  | ||||||
|         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |  | ||||||
|         onewire_write_one_bit(0); |  | ||||||
| 
 |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
| 
 |  | ||||||
|         if(compare_key_ds1990(key)) { |  | ||||||
|             result = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWriter::write_TM2004(iButtonKey* key) { |  | ||||||
|     uint8_t answer; |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |  | ||||||
|         FURI_CRITICAL_ENTER(); |  | ||||||
| 
 |  | ||||||
|         // write rom, addr is 0x0000
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         onewire_master->write(TM2004::CMD_WRITE_ROM); |  | ||||||
|         onewire_master->write(0x00); |  | ||||||
|         onewire_master->write(0x00); |  | ||||||
| 
 |  | ||||||
|         // write key
 |  | ||||||
|         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { |  | ||||||
|             // write key byte
 |  | ||||||
|             onewire_master->write(key->get_data()[i]); |  | ||||||
|             answer = onewire_master->read(); |  | ||||||
|             // TODO: check answer CRC
 |  | ||||||
| 
 |  | ||||||
|             // pulse indicating that data is correct
 |  | ||||||
|             delay_us(600); |  | ||||||
|             onewire_write_one_bit(1, 50000); |  | ||||||
| 
 |  | ||||||
|             // read writed key byte
 |  | ||||||
|             answer = onewire_master->read(); |  | ||||||
| 
 |  | ||||||
|             // check that writed and readed are same
 |  | ||||||
|             if(key->get_data()[i] != answer) { |  | ||||||
|                 result = false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!compare_key_ds1990(key)) { |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         onewire_master->reset(); |  | ||||||
| 
 |  | ||||||
|         FURI_CRITICAL_EXIT(); |  | ||||||
|     } else { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool KeyWriter::write_TM01(iButtonKey* key) { |  | ||||||
|     /*bool result = true;
 |  | ||||||
| 
 |  | ||||||
|     // TODO test and encoding
 |  | ||||||
|     FURI_CRITICAL_ENTER(); |  | ||||||
| 
 |  | ||||||
|     // unlock
 |  | ||||||
|     onewire_master->reset(); |  | ||||||
|     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(1, 10000); |  | ||||||
| 
 |  | ||||||
|     // write key
 |  | ||||||
|     onewire_master->reset(); |  | ||||||
|     onewire_master->write(TM01::CMD_WRITE_ROM); |  | ||||||
| 
 |  | ||||||
|     // TODO: key types
 |  | ||||||
|     //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
 |  | ||||||
|     //} else {
 |  | ||||||
|     for(uint8_t i = 0; i < key->get_type_data_size(); i++) { |  | ||||||
|         write_byte_ds1990(key->get_data()[i]); |  | ||||||
|         delay_us(10000); |  | ||||||
|     } |  | ||||||
|     //}
 |  | ||||||
| 
 |  | ||||||
|     // lock
 |  | ||||||
|     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(0, 10000); |  | ||||||
| 
 |  | ||||||
|     FURI_CRITICAL_EXIT(); |  | ||||||
| 
 |  | ||||||
|     if(!compare_key_ds1990(key)) { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_CRITICAL_ENTER(); |  | ||||||
| 
 |  | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyMetakom || |  | ||||||
|        key->get_key_type() == iButtonKeyType::KeyCyfral) { |  | ||||||
|         onewire_master->reset(); |  | ||||||
|         if(key->get_key_type() == iButtonKeyType::KeyCyfral) |  | ||||||
|             onewire_master->write(TM01::CMD_SWITCH_TO_CYFRAL); |  | ||||||
|         else |  | ||||||
|             onewire_master->write(TM01::CMD_SWITCH_TO_METAKOM); |  | ||||||
|         onewire_write_one_bit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     FURI_CRITICAL_EXIT(); |  | ||||||
| 
 |  | ||||||
|     return result;*/ |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWriter::onewire_write_one_bit(bool value, uint32_t delay) { |  | ||||||
|     onewire_master->write_bit(value); |  | ||||||
|     delay_us(delay); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void KeyWriter::write_byte_ds1990(uint8_t data) { |  | ||||||
|     for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { |  | ||||||
|         onewire_master->write_bit(data & 1); |  | ||||||
|         delay_us(5000); |  | ||||||
|         data = data >> 1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <one_wire_master.h> |  | ||||||
| 
 |  | ||||||
| class KeyWriter { |  | ||||||
| public: |  | ||||||
|     enum class Error : uint8_t { |  | ||||||
|         OK, |  | ||||||
|         SAME_KEY, |  | ||||||
|         NO_DETECT, |  | ||||||
|         CANNOT_WRITE, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     KeyWriter(OneWireMaster* onewire_master); |  | ||||||
|     ~KeyWriter(); |  | ||||||
| 
 |  | ||||||
|     KeyWriter::Error write(iButtonKey* key); |  | ||||||
|     void start(); |  | ||||||
|     void stop(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     OneWireMaster* onewire_master; |  | ||||||
| 
 |  | ||||||
|     KeyWriter::Error write_internal(iButtonKey* key); |  | ||||||
|     bool compare_key_ds1990(iButtonKey* key); |  | ||||||
| 
 |  | ||||||
|     // write strategy
 |  | ||||||
|     bool write_1990_1(iButtonKey* key); |  | ||||||
|     bool write_1990_2(iButtonKey* key); |  | ||||||
|     bool write_TM2004(iButtonKey* key); |  | ||||||
|     bool write_TM01(iButtonKey* key); |  | ||||||
| 
 |  | ||||||
|     void onewire_write_one_bit(bool value, uint32_t delay = 10000); |  | ||||||
|     void write_byte_ds1990(uint8_t data); |  | ||||||
| }; |  | ||||||
| @ -1,191 +0,0 @@ | |||||||
| #include "metakom_decoder.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| bool MetakomDecoder::read(uint8_t* _data, uint8_t data_size) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(ready) { |  | ||||||
|         memcpy(_data, &key_data, 4); |  | ||||||
|         reset_state(); |  | ||||||
|         result = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MetakomDecoder::process_front(bool polarity, uint32_t time) { |  | ||||||
|     if(max_period == 0) { |  | ||||||
|         max_period = 230 * (SystemCoreClock / 1000000.0f); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(ready) return; |  | ||||||
| 
 |  | ||||||
|     uint32_t high_time = 0; |  | ||||||
|     uint32_t low_time = 0; |  | ||||||
| 
 |  | ||||||
|     switch(state) { |  | ||||||
|     case State::WAIT_PERIOD_SYNC: |  | ||||||
|         if(process_bit(polarity, time, &high_time, &low_time)) { |  | ||||||
|             period_sample_data[period_sample_index] = high_time + low_time; |  | ||||||
|             period_sample_index++; |  | ||||||
| 
 |  | ||||||
|             if(period_sample_index == period_sample_count) { |  | ||||||
|                 for(uint8_t i = 0; i < period_sample_count; i++) { |  | ||||||
|                     period_time += period_sample_data[i]; |  | ||||||
|                 }; |  | ||||||
|                 period_time /= period_sample_count; |  | ||||||
| 
 |  | ||||||
|                 state = State::WAIT_START_BIT; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         break; |  | ||||||
|     case State::WAIT_START_BIT: |  | ||||||
|         if(process_bit(polarity, time, &high_time, &low_time)) { |  | ||||||
|             tmp_counter++; |  | ||||||
|             if(high_time > period_time) { |  | ||||||
|                 tmp_counter = 0; |  | ||||||
|                 state = State::WAIT_START_WORD; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(tmp_counter > 40) { |  | ||||||
|                 reset_state(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         break; |  | ||||||
|     case State::WAIT_START_WORD: |  | ||||||
|         if(process_bit(polarity, time, &high_time, &low_time)) { |  | ||||||
|             if(low_time < (period_time / 2)) { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b0; |  | ||||||
|             } else { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b1; |  | ||||||
|             } |  | ||||||
|             tmp_counter++; |  | ||||||
| 
 |  | ||||||
|             if(tmp_counter == 3) { |  | ||||||
|                 if(tmp_data == 0b010) { |  | ||||||
|                     tmp_counter = 0; |  | ||||||
|                     tmp_data = 0; |  | ||||||
|                     state = State::READ_WORD; |  | ||||||
|                 } else { |  | ||||||
|                     reset_state(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case State::READ_WORD: |  | ||||||
|         if(process_bit(polarity, time, &high_time, &low_time)) { |  | ||||||
|             if(low_time < (period_time / 2)) { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b0; |  | ||||||
|             } else { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b1; |  | ||||||
|             } |  | ||||||
|             tmp_counter++; |  | ||||||
| 
 |  | ||||||
|             if(tmp_counter == 8) { |  | ||||||
|                 if(parity_check(tmp_data)) { |  | ||||||
|                     key_data = (key_data << 8) | tmp_data; |  | ||||||
|                     key_data_index++; |  | ||||||
|                     tmp_data = 0; |  | ||||||
|                     tmp_counter = 0; |  | ||||||
| 
 |  | ||||||
|                     if(key_data_index == 4) { |  | ||||||
|                         // check for stop bit
 |  | ||||||
|                         if(high_time > period_time) { |  | ||||||
|                             state = State::READ_STOP_WORD; |  | ||||||
|                         } else { |  | ||||||
|                             reset_state(); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     reset_state(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case State::READ_STOP_WORD: |  | ||||||
|         if(process_bit(polarity, time, &high_time, &low_time)) { |  | ||||||
|             if(low_time < (period_time / 2)) { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b0; |  | ||||||
|             } else { |  | ||||||
|                 tmp_data = (tmp_data << 1) | 0b1; |  | ||||||
|             } |  | ||||||
|             tmp_counter++; |  | ||||||
| 
 |  | ||||||
|             if(tmp_counter == 3) { |  | ||||||
|                 if(tmp_data == 0b010) { |  | ||||||
|                     ready = true; |  | ||||||
|                 } else { |  | ||||||
|                     reset_state(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| MetakomDecoder::MetakomDecoder() { |  | ||||||
|     reset_state(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void MetakomDecoder::reset_state() { |  | ||||||
|     ready = false; |  | ||||||
|     period_sample_index = 0; |  | ||||||
|     period_time = 0; |  | ||||||
| 
 |  | ||||||
|     tmp_counter = 0; |  | ||||||
|     tmp_data = 0; |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < period_sample_count; i++) { |  | ||||||
|         period_sample_data[i] = 0; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     state = State::WAIT_PERIOD_SYNC; |  | ||||||
|     bit_state = BitState::WAIT_FRONT_LOW; |  | ||||||
| 
 |  | ||||||
|     key_data = 0; |  | ||||||
|     key_data_index = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool MetakomDecoder::parity_check(uint8_t data) { |  | ||||||
|     uint8_t ones_count = 0; |  | ||||||
|     bool result; |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < 8; i++) { |  | ||||||
|         if((data >> i) & 0b00000001) { |  | ||||||
|             ones_count++; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     result = (ones_count % 2 == 0); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool MetakomDecoder::process_bit( |  | ||||||
|     bool polarity, |  | ||||||
|     uint32_t time, |  | ||||||
|     uint32_t* high_time, |  | ||||||
|     uint32_t* low_time) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     switch(bit_state) { |  | ||||||
|     case BitState::WAIT_FRONT_LOW: |  | ||||||
|         if(polarity == false) { |  | ||||||
|             *low_time = low_time_storage; |  | ||||||
|             *high_time = time; |  | ||||||
|             result = true; |  | ||||||
|             bit_state = BitState::WAIT_FRONT_HIGH; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case BitState::WAIT_FRONT_HIGH: |  | ||||||
|         if(polarity == true) { |  | ||||||
|             low_time_storage = time; |  | ||||||
|             bit_state = BitState::WAIT_FRONT_LOW; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,54 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| class MetakomDecoder { |  | ||||||
| public: |  | ||||||
|     bool read(uint8_t* data, uint8_t data_size); |  | ||||||
|     void process_front(bool polarity, uint32_t time); |  | ||||||
| 
 |  | ||||||
|     MetakomDecoder(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     enum class BitState : uint8_t { |  | ||||||
|         WAIT_FRONT_HIGH, |  | ||||||
|         WAIT_FRONT_LOW, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     BitState bit_state; |  | ||||||
| 
 |  | ||||||
|     enum class State : uint8_t { |  | ||||||
|         WAIT_PERIOD_SYNC, |  | ||||||
|         WAIT_START_BIT, |  | ||||||
|         WAIT_START_WORD, |  | ||||||
|         READ_WORD, |  | ||||||
|         READ_STOP_WORD, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     State state; |  | ||||||
| 
 |  | ||||||
|     // high + low period time
 |  | ||||||
|     uint32_t period_time; |  | ||||||
|     uint32_t low_time_storage; |  | ||||||
| 
 |  | ||||||
|     static const uint8_t period_sample_count = 10; |  | ||||||
|     uint8_t period_sample_index; |  | ||||||
|     uint32_t period_sample_data[period_sample_count]; |  | ||||||
| 
 |  | ||||||
|     // ready flag, key is readed and valid
 |  | ||||||
|     std::atomic<bool> ready; |  | ||||||
| 
 |  | ||||||
|     // max period, 230us x clock per us
 |  | ||||||
|     uint32_t max_period; |  | ||||||
| 
 |  | ||||||
|     uint8_t tmp_data; |  | ||||||
|     uint8_t tmp_counter; |  | ||||||
| 
 |  | ||||||
|     uint32_t key_data; |  | ||||||
|     uint8_t key_data_index; |  | ||||||
| 
 |  | ||||||
|     void reset_state(); |  | ||||||
|     bool parity_check(uint8_t data); |  | ||||||
| 
 |  | ||||||
|     bool process_bit(bool polarity, uint32_t time, uint32_t* high_time, uint32_t* low_time); |  | ||||||
| }; |  | ||||||
| @ -1,52 +0,0 @@ | |||||||
| #include "pulse_sequencer.h" |  | ||||||
| 
 |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| 
 |  | ||||||
| void PulseSequencer::set_periods( |  | ||||||
|     uint32_t* _periods, |  | ||||||
|     uint16_t _periods_count, |  | ||||||
|     bool _pin_start_state) { |  | ||||||
|     periods = _periods; |  | ||||||
|     periods_count = _periods_count; |  | ||||||
|     pin_start_state = _pin_start_state; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void PulseSequencer::start() { |  | ||||||
|     period_index = 1; |  | ||||||
|     pin_state = pin_start_state; |  | ||||||
|     hal_gpio_write(&ibutton_gpio, pin_state); |  | ||||||
|     pin_state = !pin_state; |  | ||||||
| 
 |  | ||||||
|     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedVeryHigh); |  | ||||||
|     furi_hal_ibutton_emulate_start( |  | ||||||
|         periods[period_index], PulseSequencer::timer_elapsed_callback, this); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void PulseSequencer::stop() { |  | ||||||
|     furi_hal_ibutton_emulate_stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| PulseSequencer::~PulseSequencer() { |  | ||||||
|     stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void PulseSequencer::timer_elapsed_callback(void* context) { |  | ||||||
|     PulseSequencer* self = static_cast<PulseSequencer*>(context); |  | ||||||
| 
 |  | ||||||
|     furi_hal_ibutton_emulate_set_next(self->periods[self->period_index]); |  | ||||||
| 
 |  | ||||||
|     if(self->period_index == 0) { |  | ||||||
|         self->pin_state = self->pin_start_state; |  | ||||||
|     } else { |  | ||||||
|         self->pin_state = !self->pin_state; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     hal_gpio_write(&ibutton_gpio, self->pin_state); |  | ||||||
| 
 |  | ||||||
|     self->period_index++; |  | ||||||
| 
 |  | ||||||
|     if(self->period_index == self->periods_count) { |  | ||||||
|         self->period_index = 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| class PulseSequencer { |  | ||||||
| public: |  | ||||||
|     void set_periods(uint32_t* periods, uint16_t periods_count, bool pin_start_state); |  | ||||||
|     void start(); |  | ||||||
|     void stop(); |  | ||||||
| 
 |  | ||||||
|     ~PulseSequencer(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     uint16_t period_index; |  | ||||||
|     uint16_t periods_count; |  | ||||||
|     uint32_t* periods; |  | ||||||
|     bool pin_start_state; |  | ||||||
|     bool pin_state; |  | ||||||
| 
 |  | ||||||
|     void init_timer(uint32_t period); |  | ||||||
| 
 |  | ||||||
|     void reset_period_index(PulseSequencer* _this); |  | ||||||
| 
 |  | ||||||
|     void (*callback_pointer)(void*, void*); |  | ||||||
| 
 |  | ||||||
|     static void timer_elapsed_callback(void* comp_ctx); |  | ||||||
| }; |  | ||||||
| @ -1,6 +1,6 @@ | |||||||
| #include "ibutton_app.h" | #include "ibutton_app.h" | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <callback-connector.h> | #include <furi_hal.h> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <flipper_format/flipper_format.h> | #include <flipper_format/flipper_format.h> | ||||||
| @ -42,7 +42,9 @@ iButtonApp::iButtonApp() | |||||||
|     , storage{"storage"} |     , storage{"storage"} | ||||||
|     , dialogs{"dialogs"} { |     , dialogs{"dialogs"} { | ||||||
|     furi_hal_power_insomnia_enter(); |     furi_hal_power_insomnia_enter(); | ||||||
|     key_worker = new KeyWorker(&ibutton_gpio); |     key = ibutton_key_alloc(); | ||||||
|  |     key_worker = ibutton_worker_alloc(); | ||||||
|  |     ibutton_worker_start_thread(key_worker); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| iButtonApp::~iButtonApp() { | iButtonApp::~iButtonApp() { | ||||||
| @ -50,7 +52,10 @@ iButtonApp::~iButtonApp() { | |||||||
|         delete it->second; |         delete it->second; | ||||||
|     } |     } | ||||||
|     scenes.clear(); |     scenes.clear(); | ||||||
|     delete key_worker; | 
 | ||||||
|  |     ibutton_worker_stop_thread(key_worker); | ||||||
|  |     ibutton_worker_free(key_worker); | ||||||
|  |     ibutton_key_free(key); | ||||||
| 
 | 
 | ||||||
|     furi_hal_power_insomnia_exit(); |     furi_hal_power_insomnia_exit(); | ||||||
| } | } | ||||||
| @ -112,17 +117,12 @@ iButtonApp::Scene iButtonApp::get_previous_scene() { | |||||||
|     return scene; |     return scene; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const GpioPin* iButtonApp::get_ibutton_pin() { | iButtonWorker* iButtonApp::get_key_worker() { | ||||||
|     // TODO open record
 |  | ||||||
|     return &ibutton_gpio; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| KeyWorker* iButtonApp::get_key_worker() { |  | ||||||
|     return key_worker; |     return key_worker; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| iButtonKey* iButtonApp::get_key() { | iButtonKey* iButtonApp::get_key() { | ||||||
|     return &key; |     return key; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| char* iButtonApp::get_file_name() { | char* iButtonApp::get_file_name() { | ||||||
| @ -201,10 +201,11 @@ bool iButtonApp::save_key(const char* key_name) { | |||||||
|         if(!delete_key()) break; |         if(!delete_key()) break; | ||||||
| 
 | 
 | ||||||
|         // Save the key
 |         // Save the key
 | ||||||
|         key.set_name(key_name); |         ibutton_key_set_name(key, key_name); | ||||||
| 
 | 
 | ||||||
|         // Set full file name, for new key
 |         // Set full file name, for new key
 | ||||||
|         string_printf(key_file_name, "%s/%s%s", app_folder, key.get_name(), app_extension); |         string_printf( | ||||||
|  |             key_file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension); | ||||||
| 
 | 
 | ||||||
|         // Open file for write
 |         // Open file for write
 | ||||||
|         if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break; |         if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break; | ||||||
| @ -215,7 +216,7 @@ bool iButtonApp::save_key(const char* key_name) { | |||||||
|         // Write key type
 |         // Write key type
 | ||||||
|         if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom")) |         if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom")) | ||||||
|             break; |             break; | ||||||
|         const char* key_type = key.get_key_type_string_by_type(key.get_key_type()); |         const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key)); | ||||||
|         if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break; |         if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break; | ||||||
| 
 | 
 | ||||||
|         // Write data
 |         // Write data
 | ||||||
| @ -223,7 +224,8 @@ bool iButtonApp::save_key(const char* key_name) { | |||||||
|                file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8")) |                file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8")) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         if(!flipper_format_write_hex(file, "Data", key.get_data(), key.get_type_data_size())) |         if(!flipper_format_write_hex( | ||||||
|  |                file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key))) | ||||||
|             break; |             break; | ||||||
|         result = true; |         result = true; | ||||||
| 
 | 
 | ||||||
| @ -258,15 +260,15 @@ bool iButtonApp::load_key_data(string_t key_path) { | |||||||
|         // key type
 |         // key type
 | ||||||
|         iButtonKeyType type; |         iButtonKeyType type; | ||||||
|         if(!flipper_format_read_string(file, "Key type", data)) break; |         if(!flipper_format_read_string(file, "Key type", data)) break; | ||||||
|         if(!key.get_key_type_by_type_string(string_get_cstr(data), &type)) break; |         if(!ibutton_key_get_type_by_string(string_get_cstr(data), &type)) break; | ||||||
| 
 | 
 | ||||||
|         // key data
 |         // key data
 | ||||||
|         uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0}; |         uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0}; | ||||||
|         if(!flipper_format_read_hex(file, "Data", key_data, key.get_type_data_size_by_type(type))) |         if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type))) | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         key.set_type(type); |         ibutton_key_set_type(key, type); | ||||||
|         key.set_data(key_data, IBUTTON_KEY_DATA_SIZE); |         ibutton_key_set_data(key, key_data, IBUTTON_KEY_DATA_SIZE); | ||||||
| 
 | 
 | ||||||
|         result = true; |         result = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| @ -290,7 +292,7 @@ bool iButtonApp::load_key(const char* key_name) { | |||||||
|     result = load_key_data(key_path); |     result = load_key_data(key_path); | ||||||
|     if(result) { |     if(result) { | ||||||
|         path_extract_filename_no_ext(key_name, key_path); |         path_extract_filename_no_ext(key_name, key_path); | ||||||
|         get_key()->set_name(string_get_cstr(key_path)); |         ibutton_key_set_name(key, string_get_cstr(key_path)); | ||||||
|     } |     } | ||||||
|     string_clear(key_path); |     string_clear(key_path); | ||||||
|     return result; |     return result; | ||||||
| @ -306,7 +308,7 @@ bool iButtonApp::load_key() { | |||||||
|         app_extension, |         app_extension, | ||||||
|         get_file_name(), |         get_file_name(), | ||||||
|         get_file_name_size(), |         get_file_name_size(), | ||||||
|         get_key()->get_name()); |         ibutton_key_get_name_p(key)); | ||||||
| 
 | 
 | ||||||
|     if(res) { |     if(res) { | ||||||
|         string_t key_str; |         string_t key_str; | ||||||
| @ -316,7 +318,7 @@ bool iButtonApp::load_key() { | |||||||
| 
 | 
 | ||||||
|         result = load_key_data(key_str); |         result = load_key_data(key_str); | ||||||
|         if(result) { |         if(result) { | ||||||
|             get_key()->set_name(get_file_name()); |             ibutton_key_set_name(key, get_file_name()); | ||||||
|         } |         } | ||||||
|         string_clear(key_str); |         string_clear(key_str); | ||||||
|     } |     } | ||||||
| @ -328,7 +330,8 @@ bool iButtonApp::delete_key() { | |||||||
|     string_t file_name; |     string_t file_name; | ||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); |     string_init_printf( | ||||||
|  |         file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension); | ||||||
|     result = storage_simply_remove(storage, string_get_cstr(file_name)); |     result = storage_simply_remove(storage, string_get_cstr(file_name)); | ||||||
|     string_clear(file_name); |     string_clear(file_name); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,17 +22,10 @@ | |||||||
| #include "scene/ibutton_scene_select_key.h" | #include "scene/ibutton_scene_select_key.h" | ||||||
| #include "scene/ibutton_scene_add_type.h" | #include "scene/ibutton_scene_add_type.h" | ||||||
| #include "scene/ibutton_scene_add_value.h" | #include "scene/ibutton_scene_add_value.h" | ||||||
| 
 | #include <one_wire/ibutton/ibutton_worker.h> | ||||||
| #include "helpers/key_worker.h" |  | ||||||
| 
 |  | ||||||
| #include "one_wire_master.h" |  | ||||||
| #include "maxim_crc.h" |  | ||||||
| #include "ibutton_key.h" |  | ||||||
| 
 |  | ||||||
| #include <notification/notification_messages.h> | #include <notification/notification_messages.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| 
 |  | ||||||
| #include <record_controller.hpp> | #include <record_controller.hpp> | ||||||
| 
 | 
 | ||||||
| class iButtonApp { | class iButtonApp { | ||||||
| @ -75,7 +68,7 @@ public: | |||||||
|     Scene get_previous_scene(); |     Scene get_previous_scene(); | ||||||
| 
 | 
 | ||||||
|     const GpioPin* get_ibutton_pin(); |     const GpioPin* get_ibutton_pin(); | ||||||
|     KeyWorker* get_key_worker(); |     iButtonWorker* get_key_worker(); | ||||||
|     iButtonKey* get_key(); |     iButtonKey* get_key(); | ||||||
| 
 | 
 | ||||||
|     void notify_green_blink(); |     void notify_green_blink(); | ||||||
| @ -127,9 +120,8 @@ private: | |||||||
|         {Scene::SceneAddValue, new iButtonSceneAddValue()}, |         {Scene::SceneAddValue, new iButtonSceneAddValue()}, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     KeyWorker* key_worker; |     iButtonWorker* key_worker; | ||||||
| 
 |     iButtonKey* key; | ||||||
|     iButtonKey key; |  | ||||||
| 
 | 
 | ||||||
|     RecordController<NotificationApp> notification; |     RecordController<NotificationApp> notification; | ||||||
|     RecordController<Storage> storage; |     RecordController<Storage> storage; | ||||||
|  | |||||||
							
								
								
									
										308
									
								
								applications/ibutton/ibutton_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								applications/ibutton/ibutton_cli.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,308 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include <one_wire/ibutton/ibutton_worker.h> | ||||||
|  | #include <one_wire/one_wire_host.h> | ||||||
|  | 
 | ||||||
|  | void ibutton_cli(Cli* cli, string_t args, void* context); | ||||||
|  | void onewire_cli(Cli* cli, string_t args, void* context); | ||||||
|  | 
 | ||||||
|  | // app cli function
 | ||||||
|  | void ibutton_on_system_start() { | ||||||
|  | #ifdef SRV_CLI | ||||||
|  |     Cli* cli = furi_record_open("cli"); | ||||||
|  |     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); | ||||||
|  |     cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); | ||||||
|  |     furi_record_close("cli"); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_cli_print_usage() { | ||||||
|  |     printf("Usage:\r\n"); | ||||||
|  |     printf("ikey read\r\n"); | ||||||
|  |     printf("ikey emulate <key_type> <key_data>\r\n"); | ||||||
|  |     printf("ikey write Dallas <key_data>\r\n"); | ||||||
|  |     printf("\t<key_type> choose from:\r\n"); | ||||||
|  |     printf("\tDallas (8 bytes key_data)\r\n"); | ||||||
|  |     printf("\tCyfral (2 bytes key_data)\r\n"); | ||||||
|  |     printf("\tMetakom (4 bytes key_data)\r\n"); | ||||||
|  |     printf("\t<key_data> are hex-formatted\r\n"); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool ibutton_cli_get_key_type(string_t data, iButtonKeyType* type) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(string_cmp_str(data, "Dallas") == 0 || string_cmp_str(data, "dallas") == 0) { | ||||||
|  |         result = true; | ||||||
|  |         *type = iButtonKeyDS1990; | ||||||
|  |     } else if(string_cmp_str(data, "Cyfral") == 0 || string_cmp_str(data, "cyfral") == 0) { | ||||||
|  |         result = true; | ||||||
|  |         *type = iButtonKeyCyfral; | ||||||
|  |     } else if(string_cmp_str(data, "Metakom") == 0 || string_cmp_str(data, "metakom") == 0) { | ||||||
|  |         result = true; | ||||||
|  |         *type = iButtonKeyMetakom; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_cli_print_key_data(iButtonKey* key) { | ||||||
|  |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
|  |     iButtonKeyType type = ibutton_key_get_type(key); | ||||||
|  | 
 | ||||||
|  |     printf("%s ", ibutton_key_get_string_by_type(type)); | ||||||
|  |     for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) { | ||||||
|  |         printf("%02X", key_data[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     printf("\r\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) | ||||||
|  | 
 | ||||||
|  | static void ibutton_cli_worker_read_cb(void* context) { | ||||||
|  |     osEventFlagsId_t event = context; | ||||||
|  |     osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_cli_read(Cli* cli) { | ||||||
|  |     iButtonKey* key = ibutton_key_alloc(); | ||||||
|  |     iButtonWorker* worker = ibutton_worker_alloc(); | ||||||
|  |     osEventFlagsId_t event = osEventFlagsNew(NULL); | ||||||
|  | 
 | ||||||
|  |     ibutton_worker_start_thread(worker); | ||||||
|  |     ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event); | ||||||
|  | 
 | ||||||
|  |     ibutton_worker_read_start(worker, key); | ||||||
|  |     while(true) { | ||||||
|  |         uint32_t flags = osEventFlagsWait(event, EVENT_FLAG_IBUTTON_COMPLETE, osFlagsWaitAny, 100); | ||||||
|  | 
 | ||||||
|  |         if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { | ||||||
|  |             ibutton_cli_print_key_data(key); | ||||||
|  | 
 | ||||||
|  |             if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |                 if(!ibutton_key_dallas_crc_is_valid(key)) { | ||||||
|  |                     printf("Warning: invalid CRC\r\n"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(!ibutton_key_dallas_is_1990_key(key)) { | ||||||
|  |                     printf("Warning: not a key\r\n"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(cli_cmd_interrupt_received(cli)) break; | ||||||
|  |     } | ||||||
|  |     ibutton_worker_stop(worker); | ||||||
|  | 
 | ||||||
|  |     ibutton_worker_stop_thread(worker); | ||||||
|  |     ibutton_worker_free(worker); | ||||||
|  |     ibutton_key_free(key); | ||||||
|  | 
 | ||||||
|  |     osEventFlagsDelete(event); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     osEventFlagsId_t event; | ||||||
|  |     iButtonWorkerWriteResult result; | ||||||
|  | } iButtonWriteContext; | ||||||
|  | 
 | ||||||
|  | static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | ||||||
|  |     iButtonWriteContext* write_context = (iButtonWriteContext*)context; | ||||||
|  |     write_context->result = result; | ||||||
|  |     osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_cli_write(Cli* cli, string_t args) { | ||||||
|  |     iButtonKey* key = ibutton_key_alloc(); | ||||||
|  |     iButtonWorker* worker = ibutton_worker_alloc(); | ||||||
|  |     iButtonKeyType type; | ||||||
|  |     iButtonWriteContext write_context; | ||||||
|  |     uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; | ||||||
|  |     string_t data; | ||||||
|  | 
 | ||||||
|  |     write_context.event = osEventFlagsNew(NULL); | ||||||
|  | 
 | ||||||
|  |     string_init(data); | ||||||
|  |     ibutton_worker_start_thread(worker); | ||||||
|  |     ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!args_read_string_and_trim(args, data)) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!ibutton_cli_get_key_type(data, &type)) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(type != iButtonKeyDS1990) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ibutton_key_set_type(key, type); | ||||||
|  |         ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); | ||||||
|  | 
 | ||||||
|  |         printf("Writing key "); | ||||||
|  |         ibutton_cli_print_key_data(key); | ||||||
|  |         printf("Press Ctrl+C to abort\r\n"); | ||||||
|  | 
 | ||||||
|  |         ibutton_worker_write_start(worker, key); | ||||||
|  |         while(true) { | ||||||
|  |             uint32_t flags = osEventFlagsWait( | ||||||
|  |                 write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, osFlagsWaitAny, 100); | ||||||
|  | 
 | ||||||
|  |             if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { | ||||||
|  |                 if(write_context.result == iButtonWorkerWriteSameKey || | ||||||
|  |                    write_context.result == iButtonWorkerWriteOK) { | ||||||
|  |                     printf("Write success\r\n"); | ||||||
|  |                     break; | ||||||
|  |                 } else if(write_context.result == iButtonWorkerWriteCannotWrite) { | ||||||
|  |                     printf("Write fail\r\n"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(cli_cmd_interrupt_received(cli)) break; | ||||||
|  |         } | ||||||
|  |         ibutton_worker_stop(worker); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(data); | ||||||
|  |     ibutton_worker_stop_thread(worker); | ||||||
|  |     ibutton_worker_free(worker); | ||||||
|  |     ibutton_key_free(key); | ||||||
|  | 
 | ||||||
|  |     osEventFlagsDelete(write_context.event); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void ibutton_cli_emulate(Cli* cli, string_t args) { | ||||||
|  |     iButtonKey* key = ibutton_key_alloc(); | ||||||
|  |     iButtonWorker* worker = ibutton_worker_alloc(); | ||||||
|  |     iButtonKeyType type; | ||||||
|  |     uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; | ||||||
|  |     string_t data; | ||||||
|  | 
 | ||||||
|  |     string_init(data); | ||||||
|  |     ibutton_worker_start_thread(worker); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!args_read_string_and_trim(args, data)) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!ibutton_cli_get_key_type(data, &type)) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) { | ||||||
|  |             ibutton_cli_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ibutton_key_set_type(key, type); | ||||||
|  |         ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type)); | ||||||
|  | 
 | ||||||
|  |         printf("Emulating key "); | ||||||
|  |         ibutton_cli_print_key_data(key); | ||||||
|  |         printf("Press Ctrl+C to abort\r\n"); | ||||||
|  | 
 | ||||||
|  |         ibutton_worker_emulate_start(worker, key); | ||||||
|  |         while(!cli_cmd_interrupt_received(cli)) { | ||||||
|  |             delay(100); | ||||||
|  |         }; | ||||||
|  |         ibutton_worker_stop(worker); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(data); | ||||||
|  |     ibutton_worker_stop_thread(worker); | ||||||
|  |     ibutton_worker_free(worker); | ||||||
|  |     ibutton_key_free(key); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void ibutton_cli(Cli* cli, string_t args, void* context) { | ||||||
|  |     string_t cmd; | ||||||
|  |     string_init(cmd); | ||||||
|  | 
 | ||||||
|  |     if(!args_read_string_and_trim(args, cmd)) { | ||||||
|  |         string_clear(cmd); | ||||||
|  |         ibutton_cli_print_usage(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(string_cmp_str(cmd, "read") == 0) { | ||||||
|  |         ibutton_cli_read(cli); | ||||||
|  |     } else if(string_cmp_str(cmd, "write") == 0) { | ||||||
|  |         ibutton_cli_write(cli, args); | ||||||
|  |     } else if(string_cmp_str(cmd, "emulate") == 0) { | ||||||
|  |         ibutton_cli_emulate(cli, args); | ||||||
|  |     } else { | ||||||
|  |         ibutton_cli_print_usage(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(cmd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_cli_print_usage() { | ||||||
|  |     printf("Usage:\r\n"); | ||||||
|  |     printf("onewire search\r\n"); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void onewire_cli_search(Cli* cli) { | ||||||
|  |     OneWireHost* onewire = onewire_host_alloc(); | ||||||
|  |     uint8_t address[8]; | ||||||
|  |     bool done = false; | ||||||
|  | 
 | ||||||
|  |     printf("Search started\r\n"); | ||||||
|  | 
 | ||||||
|  |     onewire_host_start(onewire); | ||||||
|  |     furi_hal_power_enable_otg(); | ||||||
|  | 
 | ||||||
|  |     while(!done) { | ||||||
|  |         if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) { | ||||||
|  |             printf("Search finished\r\n"); | ||||||
|  |             onewire_host_reset_search(onewire); | ||||||
|  |             done = true; | ||||||
|  |         } else { | ||||||
|  |             printf("Found: "); | ||||||
|  |             for(uint8_t i = 0; i < 8; i++) { | ||||||
|  |                 printf("%02X", address[i]); | ||||||
|  |             } | ||||||
|  |             printf("\r\n"); | ||||||
|  |         } | ||||||
|  |         delay(100); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_power_disable_otg(); | ||||||
|  |     onewire_host_free(onewire); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_cli(Cli* cli, string_t args, void* context) { | ||||||
|  |     string_t cmd; | ||||||
|  |     string_init(cmd); | ||||||
|  | 
 | ||||||
|  |     if(!args_read_string_and_trim(args, cmd)) { | ||||||
|  |         string_clear(cmd); | ||||||
|  |         onewire_cli_print_usage(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(string_cmp_str(cmd, "search") == 0) { | ||||||
|  |         onewire_cli_search(cli); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(cmd); | ||||||
|  | } | ||||||
| @ -1,292 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <lib/toolbox/args.h> |  | ||||||
| 
 |  | ||||||
| #include "helpers/key_info.h" |  | ||||||
| #include "helpers/key_worker.h" |  | ||||||
| 
 |  | ||||||
| #include <memory> |  | ||||||
| 
 |  | ||||||
| void ibutton_cli(Cli* cli, string_t args, void* context); |  | ||||||
| void onewire_cli(Cli* cli, string_t args, void* context); |  | ||||||
| 
 |  | ||||||
| // app cli function
 |  | ||||||
| extern "C" void ibutton_on_system_start() { |  | ||||||
| #ifdef SRV_CLI |  | ||||||
|     Cli* cli = static_cast<Cli*>(furi_record_open("cli")); |  | ||||||
|     cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); |  | ||||||
|     cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); |  | ||||||
|     furi_record_close("cli"); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ibutton_cli_print_usage() { |  | ||||||
|     printf("Usage:\r\n"); |  | ||||||
|     printf("ikey read\r\n"); |  | ||||||
|     printf("ikey <write | emulate> <key_type> <key_data>\r\n"); |  | ||||||
|     printf("\t<key_type> choose from:\r\n"); |  | ||||||
|     printf("\tDallas (8 bytes key_data)\r\n"); |  | ||||||
|     printf("\tCyfral (2 bytes key_data)\r\n"); |  | ||||||
|     printf("\tMetakom (4 bytes key_data)\r\n"); |  | ||||||
|     printf("\t<key_data> are hex-formatted\r\n"); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| bool ibutton_cli_get_key_type(string_t data, iButtonKeyType* type) { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     if(string_cmp_str(data, "Dallas") == 0 || string_cmp_str(data, "dallas") == 0) { |  | ||||||
|         result = true; |  | ||||||
|         *type = iButtonKeyType::KeyDallas; |  | ||||||
|     } else if(string_cmp_str(data, "Cyfral") == 0 || string_cmp_str(data, "cyfral") == 0) { |  | ||||||
|         result = true; |  | ||||||
|         *type = iButtonKeyType::KeyCyfral; |  | ||||||
|     } else if(string_cmp_str(data, "Metakom") == 0 || string_cmp_str(data, "metakom") == 0) { |  | ||||||
|         result = true; |  | ||||||
|         *type = iButtonKeyType::KeyMetakom; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ibutton_cli_print_key_data(iButtonKey* key) { |  | ||||||
|     uint8_t* key_data = key->get_data(); |  | ||||||
|     switch(key->get_key_type()) { |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         printf( |  | ||||||
|             "Dallas %02X%02X%02X%02X%02X%02X%02X%02X\r\n", |  | ||||||
|             key_data[0], |  | ||||||
|             key_data[1], |  | ||||||
|             key_data[2], |  | ||||||
|             key_data[3], |  | ||||||
|             key_data[4], |  | ||||||
|             key_data[5], |  | ||||||
|             key_data[6], |  | ||||||
|             key_data[7]); |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyCyfral: |  | ||||||
|         printf("Cyfral %02X%02X\r\n", key_data[0], key_data[1]); |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyMetakom: |  | ||||||
|         printf("Metakom %02X%02X%02X%02X\r\n", key_data[0], key_data[1], key_data[2], key_data[3]); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ibutton_cli_read(Cli* cli) { |  | ||||||
|     iButtonKey key; |  | ||||||
|     std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio)); |  | ||||||
| 
 |  | ||||||
|     bool exit = false; |  | ||||||
| 
 |  | ||||||
|     worker->start_read(); |  | ||||||
|     printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n"); |  | ||||||
| 
 |  | ||||||
|     while(!exit) { |  | ||||||
|         exit = cli_cmd_interrupt_received(cli); |  | ||||||
| 
 |  | ||||||
|         switch(worker->read(&key)) { |  | ||||||
|         case KeyReader::Error::EMPTY: |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::CRC_ERROR: |  | ||||||
|             ibutton_cli_print_key_data(&key); |  | ||||||
|             printf("Warning: invalid CRC\r\n"); |  | ||||||
|             exit = true; |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::OK: |  | ||||||
|             ibutton_cli_print_key_data(&key); |  | ||||||
|             exit = true; |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::NOT_ARE_KEY: |  | ||||||
|             ibutton_cli_print_key_data(&key); |  | ||||||
|             printf("Warning: not a key\r\n"); |  | ||||||
|             exit = true; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         delay(100); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     worker->stop_read(); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void ibutton_cli_write(Cli* cli, string_t args) { |  | ||||||
|     iButtonKey key; |  | ||||||
|     iButtonKeyType type; |  | ||||||
|     std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio)); |  | ||||||
| 
 |  | ||||||
|     bool exit = false; |  | ||||||
|     string_t data; |  | ||||||
|     string_init(data); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_string_and_trim(args, data)) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!ibutton_cli_get_key_type(data, &type)) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     key.set_type(type); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_hex_bytes(args, key.get_data(), key.get_type_data_size())) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     printf("Writing key "); |  | ||||||
|     ibutton_cli_print_key_data(&key); |  | ||||||
|     printf("Press Ctrl+C to abort\r\n"); |  | ||||||
| 
 |  | ||||||
|     worker->start_write(); |  | ||||||
| 
 |  | ||||||
|     while(!exit) { |  | ||||||
|         exit = cli_cmd_interrupt_received(cli); |  | ||||||
| 
 |  | ||||||
|         KeyWriter::Error result = worker->write(&key); |  | ||||||
| 
 |  | ||||||
|         switch(result) { |  | ||||||
|         case KeyWriter::Error::SAME_KEY: |  | ||||||
|         case KeyWriter::Error::OK: |  | ||||||
|             printf("Write success\r\n"); |  | ||||||
|             exit = true; |  | ||||||
|             break; |  | ||||||
|         case KeyWriter::Error::NO_DETECT: |  | ||||||
|             break; |  | ||||||
|         case KeyWriter::Error::CANNOT_WRITE: |  | ||||||
|             printf("Write fail\r\n"); |  | ||||||
|             exit = true; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         delay(100); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     worker->stop_write(); |  | ||||||
| 
 |  | ||||||
|     string_clear(data); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void ibutton_cli_emulate(Cli* cli, string_t args) { |  | ||||||
|     iButtonKey key; |  | ||||||
|     iButtonKeyType type; |  | ||||||
|     std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio)); |  | ||||||
|     bool exit = false; |  | ||||||
|     string_t data; |  | ||||||
|     string_init(data); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_string_and_trim(args, data)) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!ibutton_cli_get_key_type(data, &type)) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     key.set_type(type); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_hex_bytes(args, key.get_data(), key.get_type_data_size())) { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         string_clear(data); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     printf("Emulating key "); |  | ||||||
|     ibutton_cli_print_key_data(&key); |  | ||||||
|     printf("Press Ctrl+C to abort\r\n"); |  | ||||||
| 
 |  | ||||||
|     worker->start_emulate(&key); |  | ||||||
| 
 |  | ||||||
|     while(!exit) { |  | ||||||
|         exit = cli_cmd_interrupt_received(cli); |  | ||||||
|         delay(100); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     worker->stop_emulate(); |  | ||||||
| 
 |  | ||||||
|     string_clear(data); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void ibutton_cli(Cli* cli, string_t args, void* context) { |  | ||||||
|     string_t cmd; |  | ||||||
|     string_init(cmd); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_string_and_trim(args, cmd)) { |  | ||||||
|         string_clear(cmd); |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(string_cmp_str(cmd, "read") == 0) { |  | ||||||
|         ibutton_cli_read(cli); |  | ||||||
|     } else if(string_cmp_str(cmd, "write") == 0) { |  | ||||||
|         ibutton_cli_write(cli, args); |  | ||||||
|     } else if(string_cmp_str(cmd, "emulate") == 0) { |  | ||||||
|         ibutton_cli_emulate(cli, args); |  | ||||||
|     } else { |  | ||||||
|         ibutton_cli_print_usage(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(cmd); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void onewire_cli_print_usage() { |  | ||||||
|     printf("Usage:\r\n"); |  | ||||||
|     printf("onewire search\r\n"); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void onewire_cli_search(Cli* cli) { |  | ||||||
|     OneWireMaster onewire(&ibutton_gpio); |  | ||||||
|     uint8_t address[8]; |  | ||||||
|     bool done = false; |  | ||||||
| 
 |  | ||||||
|     printf("Search started\r\n"); |  | ||||||
| 
 |  | ||||||
|     onewire.start(); |  | ||||||
|     furi_hal_power_enable_otg(); |  | ||||||
| 
 |  | ||||||
|     while(!done) { |  | ||||||
|         if(onewire.search(address, true) != 1) { |  | ||||||
|             printf("Search finished\r\n"); |  | ||||||
|             onewire.reset_search(); |  | ||||||
|             done = true; |  | ||||||
|         } else { |  | ||||||
|             printf("Found: "); |  | ||||||
|             for(uint8_t i = 0; i < 8; i++) { |  | ||||||
|                 printf("%02X", address[i]); |  | ||||||
|             } |  | ||||||
|             printf("\r\n"); |  | ||||||
|         } |  | ||||||
|         delay(100); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     furi_hal_power_disable_otg(); |  | ||||||
|     onewire.stop(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void onewire_cli(Cli* cli, string_t args, void* context) { |  | ||||||
|     string_t cmd; |  | ||||||
|     string_init(cmd); |  | ||||||
| 
 |  | ||||||
|     if(!args_read_string_and_trim(args, cmd)) { |  | ||||||
|         string_clear(cmd); |  | ||||||
|         onewire_cli_print_usage(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(string_cmp_str(cmd, "search") == 0) { |  | ||||||
|         onewire_cli_search(cli); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(cmd); |  | ||||||
| } |  | ||||||
| @ -2,6 +2,7 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <gui/modules/dialog_ex.h> | #include <gui/modules/dialog_ex.h> | ||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
|  | #include <one_wire/ibutton/ibutton_worker.h> | ||||||
| 
 | 
 | ||||||
| class iButtonApp; | class iButtonApp; | ||||||
| 
 | 
 | ||||||
| @ -16,6 +17,9 @@ public: | |||||||
|         EventTypeTextEditResult, |         EventTypeTextEditResult, | ||||||
|         EventTypeByteEditResult, |         EventTypeByteEditResult, | ||||||
|         EventTypeWidgetButtonResult, |         EventTypeWidgetButtonResult, | ||||||
|  |         EventTypeWorkerEmulated, | ||||||
|  |         EventTypeWorkerRead, | ||||||
|  |         EventTypeWorkerWrite, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // payload
 |     // payload
 | ||||||
| @ -23,6 +27,7 @@ public: | |||||||
|         uint32_t menu_index; |         uint32_t menu_index; | ||||||
|         DialogExResult dialog_result; |         DialogExResult dialog_result; | ||||||
|         GuiButtonType widget_button_result; |         GuiButtonType widget_button_result; | ||||||
|  |         iButtonWorkerWriteResult worker_write_result; | ||||||
|     } payload; |     } payload; | ||||||
| 
 | 
 | ||||||
|     // event type
 |     // event type
 | ||||||
|  | |||||||
| @ -1,95 +0,0 @@ | |||||||
| #include "ibutton_key.h" |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| uint8_t iButtonKey::get_size() { |  | ||||||
|     return IBUTTON_KEY_DATA_SIZE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void iButtonKey::set_data(uint8_t* _data, uint8_t _data_count) { |  | ||||||
|     furi_check(_data_count > 0); |  | ||||||
|     furi_check(_data_count <= get_size()); |  | ||||||
| 
 |  | ||||||
|     memset(data, 0, get_size()); |  | ||||||
|     memcpy(data, _data, _data_count); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void iButtonKey::clear_data() { |  | ||||||
|     memset(data, 0, get_size()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t* iButtonKey::get_data() { |  | ||||||
|     return data; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t iButtonKey::get_type_data_size() { |  | ||||||
|     return get_type_data_size_by_type(type); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void iButtonKey::set_name(const char* _name) { |  | ||||||
|     strlcpy(name, _name, IBUTTON_KEY_NAME_SIZE); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| char* iButtonKey::get_name() { |  | ||||||
|     return name; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void iButtonKey::set_type(iButtonKeyType _key_type) { |  | ||||||
|     type = _key_type; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| iButtonKeyType iButtonKey::get_key_type() { |  | ||||||
|     return type; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const char* iButtonKey::get_key_type_string_by_type(iButtonKeyType key_type) { |  | ||||||
|     switch(key_type) { |  | ||||||
|     case iButtonKeyType::KeyCyfral: |  | ||||||
|         return "Cyfral"; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyMetakom: |  | ||||||
|         return "Metakom"; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         return "Dallas"; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         furi_crash("Invalid iButton type"); |  | ||||||
|         return ""; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool iButtonKey::get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type) { |  | ||||||
|     if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyCyfral)) == 0) { |  | ||||||
|         *key_type = iButtonKeyType::KeyCyfral; |  | ||||||
|     } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyMetakom)) == 0) { |  | ||||||
|         *key_type = iButtonKeyType::KeyMetakom; |  | ||||||
|     } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyDallas)) == 0) { |  | ||||||
|         *key_type = iButtonKeyType::KeyDallas; |  | ||||||
|     } else { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t iButtonKey::get_type_data_size_by_type(iButtonKeyType key_type) { |  | ||||||
|     uint8_t size = 0; |  | ||||||
| 
 |  | ||||||
|     switch(key_type) { |  | ||||||
|     case iButtonKeyType::KeyCyfral: |  | ||||||
|         size = 2; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyMetakom: |  | ||||||
|         size = 4; |  | ||||||
|         break; |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         size = 8; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return size; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| iButtonKey::iButtonKey() { |  | ||||||
| } |  | ||||||
| @ -1,31 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <stdint.h> |  | ||||||
| #include "helpers/key_info.h" |  | ||||||
| 
 |  | ||||||
| class iButtonKey { |  | ||||||
| public: |  | ||||||
|     uint8_t get_size(); |  | ||||||
| 
 |  | ||||||
|     void set_data(uint8_t* data, uint8_t data_count); |  | ||||||
|     void clear_data(); |  | ||||||
|     uint8_t* get_data(); |  | ||||||
|     uint8_t get_type_data_size(); |  | ||||||
| 
 |  | ||||||
|     void set_name(const char* name); |  | ||||||
|     char* get_name(); |  | ||||||
| 
 |  | ||||||
|     void set_type(iButtonKeyType key_type); |  | ||||||
|     iButtonKeyType get_key_type(); |  | ||||||
| 
 |  | ||||||
|     const char* get_key_type_string_by_type(iButtonKeyType key_type); |  | ||||||
|     bool get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type); |  | ||||||
|     uint8_t get_type_data_size_by_type(iButtonKeyType key_type); |  | ||||||
| 
 |  | ||||||
|     iButtonKey(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     uint8_t data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; |  | ||||||
|     char name[IBUTTON_KEY_NAME_SIZE] = {0}; |  | ||||||
| 
 |  | ||||||
|     iButtonKeyType type = iButtonKeyType::KeyDallas; |  | ||||||
| }; |  | ||||||
| @ -1,7 +1,5 @@ | |||||||
| #include "ibutton_scene_add_type.h" | #include "ibutton_scene_add_type.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -10,14 +8,23 @@ typedef enum { | |||||||
|     SubmenuIndexMetakom, |     SubmenuIndexMetakom, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
|  | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneAddType::on_enter(iButtonApp* app) { | void iButtonSceneAddType::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneAddType::submenu_callback); |  | ||||||
| 
 | 
 | ||||||
|     submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, callback, app); |     submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, callback, app); |     submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, callback, app); |     submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
| @ -28,19 +35,21 @@ bool iButtonSceneAddType::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
| 
 | 
 | ||||||
|     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { |     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { | ||||||
|         submenu_item_selected = event->payload.menu_index; |         submenu_item_selected = event->payload.menu_index; | ||||||
|  |         iButtonKey* key = app->get_key(); | ||||||
|  | 
 | ||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case SubmenuIndexCyfral: |         case SubmenuIndexCyfral: | ||||||
|             app->get_key()->set_type(iButtonKeyType::KeyCyfral); |             ibutton_key_set_type(key, iButtonKeyCyfral); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDallas: |         case SubmenuIndexDallas: | ||||||
|             app->get_key()->set_type(iButtonKeyType::KeyDallas); |             ibutton_key_set_type(key, iButtonKeyDS1990); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexMetakom: |         case SubmenuIndexMetakom: | ||||||
|             app->get_key()->set_type(iButtonKeyType::KeyMetakom); |             ibutton_key_set_type(key, iButtonKeyMetakom); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         app->get_key()->set_name(""); |         ibutton_key_set_name(key, ""); | ||||||
|         app->get_key()->clear_data(); |         ibutton_key_clear_data(key); | ||||||
|         app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue); |         app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| @ -54,13 +63,3 @@ void iButtonSceneAddType::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     submenu_reset(submenu); |     submenu_reset(submenu); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeMenuSelected; |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -8,6 +8,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void submenu_callback(void* context, uint32_t index); |  | ||||||
|     uint32_t submenu_item_selected = 0; |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| @ -1,17 +1,30 @@ | |||||||
| #include "ibutton_scene_add_value.h" | #include "ibutton_scene_add_value.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
|  | static void byte_input_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeByteEditResult; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneAddValue::on_enter(iButtonApp* app) { | void iButtonSceneAddValue::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     ByteInput* byte_input = view_manager->get_byte_input(); |     ByteInput* byte_input = view_manager->get_byte_input(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneAddValue::byte_input_callback); |     iButtonKey* key = app->get_key(); | ||||||
|     memcpy(this->new_key_data, app->get_key()->get_data(), app->get_key()->get_type_data_size()); | 
 | ||||||
|  |     memcpy(this->new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)); | ||||||
|  | 
 | ||||||
|     byte_input_set_result_callback( |     byte_input_set_result_callback( | ||||||
|         byte_input, callback, NULL, app, this->new_key_data, app->get_key()->get_type_data_size()); |         byte_input, | ||||||
|  |         byte_input_callback, | ||||||
|  |         NULL, | ||||||
|  |         app, | ||||||
|  |         this->new_key_data, | ||||||
|  |         ibutton_key_get_data_size(key)); | ||||||
|  | 
 | ||||||
|     byte_input_set_header_text(byte_input, "Enter the key"); |     byte_input_set_header_text(byte_input, "Enter the key"); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput); | ||||||
| @ -21,6 +34,7 @@ bool iButtonSceneAddValue::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == iButtonEvent::Type::EventTypeByteEditResult) { |     if(event->type == iButtonEvent::Type::EventTypeByteEditResult) { | ||||||
|  |         ibutton_key_set_data(app->get_key(), this->new_key_data, IBUTTON_KEY_DATA_SIZE); | ||||||
|         DOLPHIN_DEED(DolphinDeedIbuttonAdd); |         DOLPHIN_DEED(DolphinDeedIbuttonAdd); | ||||||
|         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); |         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); | ||||||
|         consumed = true; |         consumed = true; | ||||||
| @ -36,12 +50,3 @@ void iButtonSceneAddValue::on_exit(iButtonApp* app) { | |||||||
|     byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); |     byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); | ||||||
|     byte_input_set_header_text(byte_input, {0}); |     byte_input_set_header_text(byte_input, {0}); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneAddValue::byte_input_callback(void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeByteEditResult; |  | ||||||
|     memcpy(app->get_key()->get_data(), this->new_key_data, app->get_key()->get_type_data_size()); |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton_scene_generic.h" | #include "ibutton_scene_generic.h" | ||||||
| #include "../ibutton_key.h" | #include <one_wire/ibutton/ibutton_key.h> | ||||||
| 
 | 
 | ||||||
| class iButtonSceneAddValue : public iButtonScene { | class iButtonSceneAddValue : public iButtonScene { | ||||||
| public: | public: | ||||||
| @ -9,6 +9,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void byte_input_callback(void* context); |  | ||||||
|     uint8_t new_key_data[IBUTTON_KEY_DATA_SIZE] = {}; |     uint8_t new_key_data[IBUTTON_KEY_DATA_SIZE] = {}; | ||||||
| }; | }; | ||||||
| @ -1,25 +1,31 @@ | |||||||
| #include "ibutton_scene_delete_confirm.h" | #include "ibutton_scene_delete_confirm.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | 
 | ||||||
| #include "../ibutton_event.h" | static void widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
| #include <callback-connector.h> |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         event.type = iButtonEvent::Type::EventTypeWidgetButtonResult; | ||||||
|  |         event.payload.widget_button_result = result; | ||||||
|  |         app->get_view_manager()->send_event(&event); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Widget* widget = view_manager->get_widget(); |     Widget* widget = view_manager->get_widget(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteConfirm::widget_callback); |  | ||||||
| 
 |  | ||||||
|     iButtonKey* key = app->get_key(); |     iButtonKey* key = app->get_key(); | ||||||
|     uint8_t* key_data = key->get_data(); |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
| 
 | 
 | ||||||
|     app->set_text_store("\e#Delete %s?\e#", key->get_name()); |     app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key)); | ||||||
|     widget_add_text_box_element( |     widget_add_text_box_element( | ||||||
|         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); |         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); | ||||||
|     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", callback, app); |     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app); | ||||||
|     widget_add_button_element(widget, GuiButtonTypeRight, "Delete", callback, app); |     widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); | ||||||
| 
 | 
 | ||||||
|     switch(key->get_key_type()) { |     switch(ibutton_key_get_type(key)) { | ||||||
|     case iButtonKeyType::KeyDallas: |     case iButtonKeyDS1990: | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "%02X %02X %02X %02X %02X %02X %02X %02X", |             "%02X %02X %02X %02X %02X %02X %02X %02X", | ||||||
|             key_data[0], |             key_data[0], | ||||||
| @ -33,12 +39,12 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | |||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); |             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyCyfral: |     case iButtonKeyCyfral: | ||||||
|         app->set_text_store("%02X %02X", key_data[0], key_data[1]); |         app->set_text_store("%02X %02X", key_data[0], key_data[1]); | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); |             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyMetakom: |     case iButtonKeyMetakom: | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); |             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
| @ -77,18 +83,3 @@ void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     widget_reset(widget); |     widget_reset(widget); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneDeleteConfirm::widget_callback( |  | ||||||
|     GuiButtonType result, |  | ||||||
|     InputType type, |  | ||||||
|     void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     if(type == InputTypeShort) { |  | ||||||
|         event.type = iButtonEvent::Type::EventTypeWidgetButtonResult; |  | ||||||
|         event.payload.widget_button_result = result; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void widget_callback(GuiButtonType result, InputType type, void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,19 +1,21 @@ | |||||||
| #include "ibutton_scene_delete_success.h" | #include "ibutton_scene_delete_success.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | 
 | ||||||
| #include "../ibutton_event.h" | static void popup_callback(void* context) { | ||||||
| #include "../ibutton_key.h" |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
| #include <callback-connector.h> |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) { | void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteSuccess::popup_callback); |  | ||||||
| 
 | 
 | ||||||
|     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); |     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); | ||||||
|     popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); |     popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); | ||||||
| 
 | 
 | ||||||
|     popup_set_callback(popup, callback); |     popup_set_callback(popup, popup_callback); | ||||||
|     popup_set_context(popup, app); |     popup_set_context(popup, app); | ||||||
|     popup_set_timeout(popup, 1500); |     popup_set_timeout(popup, 1500); | ||||||
|     popup_enable_timeout(popup); |     popup_enable_timeout(popup); | ||||||
| @ -42,10 +44,3 @@ void iButtonSceneDeleteSuccess::on_exit(iButtonApp* app) { | |||||||
|     popup_set_context(popup, NULL); |     popup_set_context(popup, NULL); | ||||||
|     popup_set_callback(popup, NULL); |     popup_set_callback(popup, NULL); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneDeleteSuccess::popup_callback(void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void popup_callback(void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,17 +1,21 @@ | |||||||
| #include "ibutton_scene_emulate.h" | #include "ibutton_scene_emulate.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <callback-connector.h> | 
 | ||||||
|  | static void emulate_callback(void* context, bool emulated) { | ||||||
|  |     if(emulated) { | ||||||
|  |         iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |         iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated}; | ||||||
|  |         app->get_view_manager()->send_event(&event); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneEmulate::on_enter(iButtonApp* app) { | void iButtonSceneEmulate::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|     iButtonKey* key = app->get_key(); |     iButtonKey* key = app->get_key(); | ||||||
|     uint8_t* key_data = key->get_data(); |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
|     const char* key_name = key->get_name(); |     const char* key_name = ibutton_key_get_name_p(key); | ||||||
|     uint8_t line_count = 2; |     uint8_t line_count = 2; | ||||||
|     DOLPHIN_DEED(DolphinDeedIbuttonEmulate); |     DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||||
| 
 | 
 | ||||||
| @ -21,8 +25,8 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | |||||||
|         line_count = 2; |         line_count = 2; | ||||||
|     } else { |     } else { | ||||||
|         // if not, show key data
 |         // if not, show key data
 | ||||||
|         switch(key->get_key_type()) { |         switch(ibutton_key_get_type(key)) { | ||||||
|         case iButtonKeyType::KeyDallas: |         case iButtonKeyDS1990: | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X", |                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||||
|                 key_data[0], |                 key_data[0], | ||||||
| @ -35,11 +39,11 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | |||||||
|                 key_data[7]); |                 key_data[7]); | ||||||
|             line_count = 3; |             line_count = 3; | ||||||
|             break; |             break; | ||||||
|         case iButtonKeyType::KeyCyfral: |         case iButtonKeyCyfral: | ||||||
|             app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]); |             app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]); | ||||||
|             line_count = 2; |             line_count = 2; | ||||||
|             break; |             break; | ||||||
|         case iButtonKeyType::KeyMetakom: |         case iButtonKeyMetakom: | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|                 "emulating\n%02X %02X %02X %02X", |                 "emulating\n%02X %02X %02X %02X", | ||||||
|                 key_data[0], |                 key_data[0], | ||||||
| @ -66,29 +70,28 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | |||||||
|     popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44); |     popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|     app->get_key_worker()->start_emulate(app->get_key()); | 
 | ||||||
|  |     ibutton_worker_emulate_set_callback(app->get_key_worker(), emulate_callback, app); | ||||||
|  |     ibutton_worker_emulate_start(app->get_key_worker(), key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) { | bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == iButtonEvent::Type::EventTypeTick) { |     if(event->type == iButtonEvent::Type::EventTypeWorkerEmulated) { | ||||||
|  |         app->notify_yellow_blink(); | ||||||
|  |         consumed = true; | ||||||
|  |     } else if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|  |         app->notify_red_blink(); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(app->get_key_worker()->emulated()) { |  | ||||||
|             app->notify_yellow_blink(); |  | ||||||
|         } else { |  | ||||||
|             app->notify_red_blink(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneEmulate::on_exit(iButtonApp* app) { | void iButtonSceneEmulate::on_exit(iButtonApp* app) { | ||||||
|     app->get_key_worker()->stop_emulate(); |  | ||||||
| 
 |  | ||||||
|     Popup* popup = app->get_view_manager()->get_popup(); |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
| 
 |     ibutton_worker_stop(app->get_key_worker()); | ||||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|     popup_set_icon(popup, 0, 0, NULL); |     popup_set_icon(popup, 0, 0, NULL); | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton_scene_generic.h" | #include "ibutton_scene_generic.h" | ||||||
| #include "../helpers/key_emulator.h" |  | ||||||
| 
 | 
 | ||||||
| class iButtonSceneEmulate : public iButtonScene { | class iButtonSceneEmulate : public iButtonScene { | ||||||
| public: | public: | ||||||
|  | |||||||
| @ -1,24 +1,30 @@ | |||||||
| #include "ibutton_scene_info.h" | #include "ibutton_scene_info.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | 
 | ||||||
| #include "../ibutton_event.h" | static void widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
| #include <callback-connector.h> |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         event.type = iButtonEvent::Type::EventTypeWidgetButtonResult; | ||||||
|  |         event.payload.widget_button_result = result; | ||||||
|  |         app->get_view_manager()->send_event(&event); | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneInfo::on_enter(iButtonApp* app) { | void iButtonSceneInfo::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Widget* widget = view_manager->get_widget(); |     Widget* widget = view_manager->get_widget(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneInfo::widget_callback); |  | ||||||
| 
 |  | ||||||
|     iButtonKey* key = app->get_key(); |     iButtonKey* key = app->get_key(); | ||||||
|     uint8_t* key_data = key->get_data(); |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
| 
 | 
 | ||||||
|     app->set_text_store("%s", key->get_name()); |     app->set_text_store("%s", ibutton_key_get_name_p(key)); | ||||||
|     widget_add_text_box_element( |     widget_add_text_box_element( | ||||||
|         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); |         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); | ||||||
|     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", callback, app); |     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app); | ||||||
| 
 | 
 | ||||||
|     switch(key->get_key_type()) { |     switch(ibutton_key_get_type(key)) { | ||||||
|     case iButtonKeyType::KeyDallas: |     case iButtonKeyDS1990: | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "%02X %02X %02X %02X %02X %02X %02X %02X", |             "%02X %02X %02X %02X %02X %02X %02X %02X", | ||||||
|             key_data[0], |             key_data[0], | ||||||
| @ -32,13 +38,13 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) { | |||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); |             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyMetakom: |     case iButtonKeyMetakom: | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); |             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom"); |             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom"); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyCyfral: |     case iButtonKeyCyfral: | ||||||
|         app->set_text_store("%02X %02X", key_data[0], key_data[1]); |         app->set_text_store("%02X %02X", key_data[0], key_data[1]); | ||||||
|         widget_add_string_element( |         widget_add_string_element( | ||||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); |             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); | ||||||
| @ -71,15 +77,3 @@ void iButtonSceneInfo::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     widget_reset(widget); |     widget_reset(widget); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneInfo::widget_callback(GuiButtonType result, InputType type, void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     if(type == InputTypeShort) { |  | ||||||
|         event.type = iButtonEvent::Type::EventTypeWidgetButtonResult; |  | ||||||
|         event.payload.widget_button_result = result; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void widget_callback(GuiButtonType result, InputType type, void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,12 +1,18 @@ | |||||||
| #include "ibutton_scene_read.h" | #include "ibutton_scene_read.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
|  | static void read_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead}; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneRead::on_enter(iButtonApp* app) { | void iButtonSceneRead::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|  |     iButtonKey* key = app->get_key(); | ||||||
|  |     iButtonWorker* worker = app->get_key_worker(); | ||||||
|     DOLPHIN_DEED(DolphinDeedIbuttonRead); |     DOLPHIN_DEED(DolphinDeedIbuttonRead); | ||||||
| 
 | 
 | ||||||
|     popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); |     popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); | ||||||
| @ -14,41 +20,41 @@ void iButtonSceneRead::on_enter(iButtonApp* app) { | |||||||
|     popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); |     popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
|     app->get_key()->set_name(""); |     ibutton_key_set_name(key, ""); | ||||||
| 
 | 
 | ||||||
|     app->get_key_worker()->start_read(); |     ibutton_worker_read_set_callback(worker, read_callback, app); | ||||||
|  |     ibutton_worker_read_start(worker, key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) { | bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == iButtonEvent::Type::EventTypeTick) { |     if(event->type == iButtonEvent::Type::EventTypeWorkerRead) { | ||||||
|  |         consumed = true; | ||||||
|  | 
 | ||||||
|  |         iButtonKey* key = app->get_key(); | ||||||
|  |         if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |             if(!ibutton_key_dallas_crc_is_valid(key)) { | ||||||
|  |                 app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError); | ||||||
|  |             } else if(!ibutton_key_dallas_is_1990_key(key)) { | ||||||
|  |                 app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError); | ||||||
|  |             } else { | ||||||
|  |                 app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); | ||||||
|  |         } | ||||||
|  |     } else if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         app->notify_red_blink(); |         app->notify_red_blink(); | ||||||
| 
 |  | ||||||
|         switch(app->get_key_worker()->read(app->get_key())) { |  | ||||||
|         case KeyReader::Error::EMPTY: |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::OK: |  | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess); |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::CRC_ERROR: |  | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError); |  | ||||||
|             break; |  | ||||||
|         case KeyReader::Error::NOT_ARE_KEY: |  | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneRead::on_exit(iButtonApp* app) { | void iButtonSceneRead::on_exit(iButtonApp* app) { | ||||||
|     app->get_key_worker()->stop_read(); |  | ||||||
| 
 |  | ||||||
|     Popup* popup = app->get_view_manager()->get_popup(); |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
| 
 |     ibutton_worker_stop(app->get_key_worker()); | ||||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|     popup_set_icon(popup, 0, 0, NULL); |     popup_set_icon(popup, 0, 0, NULL); | ||||||
|  | |||||||
| @ -6,6 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| }; | }; | ||||||
| @ -1,16 +1,21 @@ | |||||||
| #include "ibutton_scene_read_crc_error.h" | #include "ibutton_scene_read_crc_error.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | #include <one_wire/maxim_crc.h> | ||||||
| #include "../ibutton_event.h" | 
 | ||||||
| #include <callback-connector.h> | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneReadCRCError::on_enter(iButtonApp* app) { | void iButtonSceneReadCRCError::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadCRCError::dialog_ex_callback); |     const uint8_t* key_data = ibutton_key_get_data_p(app->get_key()); | ||||||
| 
 |  | ||||||
|     iButtonKey* key = app->get_key(); |  | ||||||
|     uint8_t* key_data = key->get_data(); |  | ||||||
| 
 | 
 | ||||||
|     app->set_text_store( |     app->set_text_store( | ||||||
|         "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X", |         "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X", | ||||||
| @ -22,13 +27,13 @@ void iButtonSceneReadCRCError::on_enter(iButtonApp* app) { | |||||||
|         key_data[5], |         key_data[5], | ||||||
|         key_data[6], |         key_data[6], | ||||||
|         key_data[7], |         key_data[7], | ||||||
|         maxim_crc8(key_data, 7)); |         maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); | ||||||
| 
 | 
 | ||||||
|     dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); |     dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter); | ||||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|     dialog_ex_set_result_callback(dialog_ex, callback); |     dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback); | ||||||
|     dialog_ex_set_context(dialog_ex, app); |     dialog_ex_set_context(dialog_ex, app); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
| @ -66,13 +71,3 @@ void iButtonSceneReadCRCError::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     app->notify_red_off(); |     app->notify_red_off(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneReadCRCError::dialog_ex_callback(DialogExResult result, void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeDialogResult; |  | ||||||
|     event.payload.dialog_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| @ -1,13 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton_scene_generic.h" | #include "ibutton_scene_generic.h" | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| 
 | 
 | ||||||
| class iButtonSceneReadCRCError : public iButtonScene { | class iButtonSceneReadCRCError : public iButtonScene { | ||||||
| public: | public: | ||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void dialog_ex_callback(DialogExResult result, void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,16 +1,21 @@ | |||||||
| #include "ibutton_scene_read_not_key_error.h" | #include "ibutton_scene_read_not_key_error.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | #include <one_wire/maxim_crc.h> | ||||||
| #include "../ibutton_event.h" | 
 | ||||||
| #include <callback-connector.h> | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) { | void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadNotKeyError::dialog_ex_callback); |     const uint8_t* key_data = ibutton_key_get_data_p(app->get_key()); | ||||||
| 
 |  | ||||||
|     iButtonKey* key = app->get_key(); |  | ||||||
|     uint8_t* key_data = key->get_data(); |  | ||||||
| 
 | 
 | ||||||
|     app->set_text_store( |     app->set_text_store( | ||||||
|         "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X", |         "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X", | ||||||
| @ -22,13 +27,13 @@ void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) { | |||||||
|         key_data[5], |         key_data[5], | ||||||
|         key_data[6], |         key_data[6], | ||||||
|         key_data[7], |         key_data[7], | ||||||
|         maxim_crc8(key_data, 7)); |         maxim_crc8(key_data, 7, MAXIM_CRC8_INIT)); | ||||||
| 
 | 
 | ||||||
|     dialog_ex_set_header(dialog_ex, "ERROR:", 64, 10, AlignCenter, AlignCenter); |     dialog_ex_set_header(dialog_ex, "ERROR:", 64, 10, AlignCenter, AlignCenter); | ||||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); |     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|     dialog_ex_set_result_callback(dialog_ex, callback); |     dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback); | ||||||
|     dialog_ex_set_context(dialog_ex, app); |     dialog_ex_set_context(dialog_ex, app); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
| @ -66,13 +71,3 @@ void iButtonSceneReadNotKeyError::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     app->notify_red_off(); |     app->notify_red_off(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneReadNotKeyError::dialog_ex_callback(DialogExResult result, void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeDialogResult; |  | ||||||
|     event.payload.dialog_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| @ -1,13 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton_scene_generic.h" | #include "ibutton_scene_generic.h" | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| 
 | 
 | ||||||
| class iButtonSceneReadNotKeyError : public iButtonScene { | class iButtonSceneReadNotKeyError : public iButtonScene { | ||||||
| public: | public: | ||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void dialog_ex_callback(DialogExResult result, void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,21 +1,26 @@ | |||||||
| #include "ibutton_scene_read_success.h" | #include "ibutton_scene_read_success.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <callback-connector.h> | 
 | ||||||
|  | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeDialogResult; | ||||||
|  |     event.payload.dialog_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadSuccess::dialog_ex_callback); |     iButtonKey* key = app->get_key(); | ||||||
|  |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
|     DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); |     DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); | ||||||
| 
 | 
 | ||||||
|     iButtonKey* key = app->get_key(); |     switch(ibutton_key_get_type(key)) { | ||||||
|     uint8_t* key_data = key->get_data(); |     case iButtonKeyDS1990: | ||||||
| 
 |  | ||||||
|     switch(key->get_key_type()) { |  | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X", |             "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||||
|             key_data[0], |             key_data[0], | ||||||
| @ -27,10 +32,10 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | |||||||
|             key_data[6], |             key_data[6], | ||||||
|             key_data[7]); |             key_data[7]); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyCyfral: |     case iButtonKeyCyfral: | ||||||
|         app->set_text_store("Cyfral\n%02X %02X", key_data[0], key_data[1]); |         app->set_text_store("Cyfral\n%02X %02X", key_data[0], key_data[1]); | ||||||
|         break; |         break; | ||||||
|     case iButtonKeyType::KeyMetakom: |     case iButtonKeyMetakom: | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             "Metakom\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); |             "Metakom\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|         break; |         break; | ||||||
| @ -40,7 +45,7 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | |||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); |     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||||
|     dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinExcited_64x63); |     dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinExcited_64x63); | ||||||
|     dialog_ex_set_result_callback(dialog_ex, callback); |     dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback); | ||||||
|     dialog_ex_set_context(dialog_ex, app); |     dialog_ex_set_context(dialog_ex, app); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||||
| @ -80,13 +85,3 @@ void iButtonSceneReadSuccess::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     app->notify_green_off(); |     app->notify_green_off(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneReadSuccess::dialog_ex_callback(DialogExResult result, void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeDialogResult; |  | ||||||
|     event.payload.dialog_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,13 +1,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton_scene_generic.h" | #include "ibutton_scene_generic.h" | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| 
 | 
 | ||||||
| class iButtonSceneReadSuccess : public iButtonScene { | class iButtonSceneReadSuccess : public iButtonScene { | ||||||
| public: | public: | ||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void dialog_ex_callback(DialogExResult result, void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,8 +1,5 @@ | |||||||
| #include "ibutton_scene_readed_key_menu.h" | #include "ibutton_scene_readed_key_menu.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubmenuIndexWrite, |     SubmenuIndexWrite, | ||||||
| @ -11,17 +8,26 @@ typedef enum { | |||||||
|     SubmenuIndexReadNewKey, |     SubmenuIndexReadNewKey, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
|  | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback); |  | ||||||
| 
 | 
 | ||||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { |     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); |         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||||
|     } |     } | ||||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); |     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, callback, app); |     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
| @ -61,13 +67,3 @@ void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     submenu_reset(submenu); |     submenu_reset(submenu); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeMenuSelected; |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -8,6 +8,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void submenu_callback(void* context, uint32_t index); |  | ||||||
|     uint32_t submenu_item_selected = 0; |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| @ -1,18 +1,21 @@ | |||||||
| #include "ibutton_scene_save_name.h" | #include "ibutton_scene_save_name.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| #include <lib/toolbox/random_name.h> | #include <lib/toolbox/random_name.h> | ||||||
| 
 | 
 | ||||||
|  | static void text_input_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeTextEditResult; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneSaveName::on_enter(iButtonApp* app) { | void iButtonSceneSaveName::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     TextInput* text_input = view_manager->get_text_input(); |     TextInput* text_input = view_manager->get_text_input(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSaveName::text_input_callback); |  | ||||||
| 
 | 
 | ||||||
|     iButtonKey* key = app->get_key(); |     const char* key_name = ibutton_key_get_name_p(app->get_key()); | ||||||
|     const char* key_name = key->get_name(); |  | ||||||
|     bool key_name_empty = !strcmp(key_name, ""); |     bool key_name_empty = !strcmp(key_name, ""); | ||||||
| 
 | 
 | ||||||
|     if(key_name_empty) { |     if(key_name_empty) { | ||||||
| @ -23,7 +26,12 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     text_input_set_header_text(text_input, "Name the key"); |     text_input_set_header_text(text_input, "Name the key"); | ||||||
|     text_input_set_result_callback( |     text_input_set_result_callback( | ||||||
|         text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty); |         text_input, | ||||||
|  |         text_input_callback, | ||||||
|  |         app, | ||||||
|  |         app->get_text_store(), | ||||||
|  |         IBUTTON_KEY_NAME_SIZE, | ||||||
|  |         key_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = | ||||||
|         validator_is_file_alloc_init(app->app_folder, app->app_extension); |         validator_is_file_alloc_init(app->app_folder, app->app_extension); | ||||||
| @ -59,12 +67,3 @@ void iButtonSceneSaveName::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     text_input_reset(text_input); |     text_input_reset(text_input); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneSaveName::text_input_callback(void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeTextEditResult; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void text_input_callback(void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,21 +1,23 @@ | |||||||
| #include "ibutton_scene_save_success.h" | #include "ibutton_scene_save_success.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <callback-connector.h> | 
 | ||||||
|  | static void popup_callback(void* context) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) { | void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSaveSuccess::popup_callback); |  | ||||||
|     DOLPHIN_DEED(DolphinDeedIbuttonSave); |     DOLPHIN_DEED(DolphinDeedIbuttonSave); | ||||||
| 
 | 
 | ||||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); |     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||||
|     popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); |     popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); | ||||||
| 
 | 
 | ||||||
|     popup_set_callback(popup, callback); |     popup_set_callback(popup, popup_callback); | ||||||
|     popup_set_context(popup, app); |     popup_set_context(popup, app); | ||||||
|     popup_set_timeout(popup, 1500); |     popup_set_timeout(popup, 1500); | ||||||
|     popup_enable_timeout(popup); |     popup_enable_timeout(popup); | ||||||
| @ -47,10 +49,3 @@ void iButtonSceneSaveSuccess::on_exit(iButtonApp* app) { | |||||||
|     popup_set_context(popup, NULL); |     popup_set_context(popup, NULL); | ||||||
|     popup_set_callback(popup, NULL); |     popup_set_callback(popup, NULL); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneSaveSuccess::popup_callback(void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void popup_callback(void* context); |  | ||||||
| }; | }; | ||||||
| @ -1,7 +1,5 @@ | |||||||
| #include "ibutton_scene_saved_key_menu.h" | #include "ibutton_scene_saved_key_menu.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -12,18 +10,27 @@ typedef enum { | |||||||
|     SubmenuIndexInfo, |     SubmenuIndexInfo, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
|  | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) { | void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); |  | ||||||
| 
 | 
 | ||||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { |     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); |         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||||
|     } |     } | ||||||
|     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); |     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); |     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); |     submenu_add_item(submenu, "Info", SubmenuIndexInfo, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
| @ -63,13 +70,3 @@ void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     submenu_reset(submenu); |     submenu_reset(submenu); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeMenuSelected; |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -8,6 +8,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void submenu_callback(void* context, uint32_t index); |  | ||||||
|     uint32_t submenu_item_selected = 0; |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| @ -1,7 +1,5 @@ | |||||||
| #include "ibutton_scene_select_key.h" | #include "ibutton_scene_select_key.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include "../ibutton_key.h" |  | ||||||
| 
 | 
 | ||||||
| void iButtonSceneSelectKey::on_enter(iButtonApp* app) { | void iButtonSceneSelectKey::on_enter(iButtonApp* app) { | ||||||
|     // Process file_select return
 |     // Process file_select return
 | ||||||
|  | |||||||
| @ -1,8 +1,5 @@ | |||||||
| #include "ibutton_scene_start.h" | #include "ibutton_scene_start.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" |  | ||||||
| #include "../ibutton_event.h" |  | ||||||
| #include <callback-connector.h> |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubmenuIndexRead, |     SubmenuIndexRead, | ||||||
| @ -10,14 +7,23 @@ typedef enum { | |||||||
|     SubmenuIndexAdd, |     SubmenuIndexAdd, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
|  | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeMenuSelected; | ||||||
|  |     event.payload.menu_index = index; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void iButtonSceneStart::on_enter(iButtonApp* app) { | void iButtonSceneStart::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneStart::submenu_callback); |  | ||||||
| 
 | 
 | ||||||
|     submenu_add_item(submenu, "Read", SubmenuIndexRead, callback, app); |     submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, callback, app); |     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, callback, app); |     submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||||
| @ -51,13 +57,3 @@ void iButtonSceneStart::on_exit(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     submenu_reset(submenu); |     submenu_reset(submenu); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneStart::submenu_callback(void* context, uint32_t index) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeMenuSelected; |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -8,6 +8,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void submenu_callback(void* context, uint32_t index); |  | ||||||
|     uint32_t submenu_item_selected = 0; |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| @ -1,15 +1,22 @@ | |||||||
| #include "ibutton_scene_write.h" | #include "ibutton_scene_write.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | 
 | ||||||
| #include "../ibutton_event.h" | static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | ||||||
| #include "../ibutton_key.h" |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|  |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeWorkerWrite; | ||||||
|  |     event.payload.worker_write_result = result; | ||||||
|  | 
 | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneWrite::on_enter(iButtonApp* app) { | void iButtonSceneWrite::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|     iButtonKey* key = app->get_key(); |     iButtonKey* key = app->get_key(); | ||||||
|     uint8_t* key_data = key->get_data(); |     iButtonWorker* worker = app->get_key_worker(); | ||||||
|     const char* key_name = key->get_name(); |     const uint8_t* key_data = ibutton_key_get_data_p(key); | ||||||
|  |     const char* key_name = ibutton_key_get_name_p(key); | ||||||
|     uint8_t line_count = 2; |     uint8_t line_count = 2; | ||||||
| 
 | 
 | ||||||
|     // check that stored key has name
 |     // check that stored key has name
 | ||||||
| @ -18,8 +25,8 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | |||||||
|         line_count = 2; |         line_count = 2; | ||||||
|     } else { |     } else { | ||||||
|         // if not, show key data
 |         // if not, show key data
 | ||||||
|         switch(key->get_key_type()) { |         switch(ibutton_key_get_type(key)) { | ||||||
|         case iButtonKeyType::KeyDallas: |         case iButtonKeyDS1990: | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|                 "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X", |                 "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||||
|                 key_data[0], |                 key_data[0], | ||||||
| @ -32,11 +39,11 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | |||||||
|                 key_data[7]); |                 key_data[7]); | ||||||
|             line_count = 3; |             line_count = 3; | ||||||
|             break; |             break; | ||||||
|         case iButtonKeyType::KeyCyfral: |         case iButtonKeyCyfral: | ||||||
|             app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]); |             app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]); | ||||||
|             line_count = 2; |             line_count = 2; | ||||||
|             break; |             break; | ||||||
|         case iButtonKeyType::KeyMetakom: |         case iButtonKeyMetakom: | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|                 "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); |                 "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||||
|             line_count = 2; |             line_count = 2; | ||||||
| @ -60,27 +67,34 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | |||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup); | ||||||
| 
 | 
 | ||||||
|     app->get_key_worker()->start_write(); |     blink_yellow = false; | ||||||
|  |     ibutton_worker_write_set_callback(worker, ibutton_worker_write_cb, app); | ||||||
|  |     ibutton_worker_write_start(worker, key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == iButtonEvent::Type::EventTypeTick) { |     if(event->type == iButtonEvent::Type::EventTypeWorkerWrite) { | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         KeyWriter::Error result = app->get_key_worker()->write(app->get_key()); |  | ||||||
| 
 | 
 | ||||||
|         switch(result) { |         switch(event->payload.worker_write_result) { | ||||||
|         case KeyWriter::Error::SAME_KEY: |         case iButtonWorkerWriteOK: | ||||||
|         case KeyWriter::Error::OK: |         case iButtonWorkerWriteSameKey: | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess); |             app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess); | ||||||
|             break; |             break; | ||||||
|         case KeyWriter::Error::NO_DETECT: |         case iButtonWorkerWriteNoDetect: | ||||||
|             app->notify_red_blink(); |             blink_yellow = false; | ||||||
|             break; |             break; | ||||||
|         case KeyWriter::Error::CANNOT_WRITE: |         case iButtonWorkerWriteCannotWrite: | ||||||
|  |             blink_yellow = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||||
|  |         if(blink_yellow) { | ||||||
|             app->notify_yellow_blink(); |             app->notify_yellow_blink(); | ||||||
|             break; |         } else { | ||||||
|  |             app->notify_red_blink(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -89,10 +103,8 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
| 
 | 
 | ||||||
| void iButtonSceneWrite::on_exit(iButtonApp* app) { | void iButtonSceneWrite::on_exit(iButtonApp* app) { | ||||||
|     Popup* popup = app->get_view_manager()->get_popup(); |     Popup* popup = app->get_view_manager()->get_popup(); | ||||||
| 
 |     ibutton_worker_stop(app->get_key_worker()); | ||||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|     popup_set_icon(popup, 0, 0, NULL); |     popup_set_icon(popup, 0, 0, NULL); | ||||||
| 
 |  | ||||||
|     app->get_key_worker()->stop_write(); |  | ||||||
| } | } | ||||||
| @ -8,4 +8,5 @@ public: | |||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     bool blink_yellow; | ||||||
| }; | }; | ||||||
| @ -1,19 +1,22 @@ | |||||||
| #include "ibutton_scene_write_success.h" | #include "ibutton_scene_write_success.h" | ||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| #include "../ibutton_view_manager.h" | 
 | ||||||
| #include "../ibutton_event.h" | static void popup_callback(void* context) { | ||||||
| #include "../ibutton_key.h" |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
| #include <callback-connector.h> |     iButtonEvent event; | ||||||
|  |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  |     app->get_view_manager()->send_event(&event); | ||||||
|  |     app->notify_green_off(); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) { | void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) { | ||||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); |     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Popup* popup = view_manager->get_popup(); |     Popup* popup = view_manager->get_popup(); | ||||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneWriteSuccess::popup_callback); |  | ||||||
| 
 | 
 | ||||||
|     popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52); |     popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52); | ||||||
|     popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom); |     popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom); | ||||||
| 
 | 
 | ||||||
|     popup_set_callback(popup, callback); |     popup_set_callback(popup, popup_callback); | ||||||
|     popup_set_context(popup, app); |     popup_set_context(popup, app); | ||||||
|     popup_set_timeout(popup, 1500); |     popup_set_timeout(popup, 1500); | ||||||
|     popup_enable_timeout(popup); |     popup_enable_timeout(popup); | ||||||
| @ -46,11 +49,3 @@ void iButtonSceneWriteSuccess::on_exit(iButtonApp* app) { | |||||||
|     popup_set_context(popup, NULL); |     popup_set_context(popup, NULL); | ||||||
|     popup_set_callback(popup, NULL); |     popup_set_callback(popup, NULL); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void iButtonSceneWriteSuccess::popup_callback(void* context) { |  | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |  | ||||||
|     iButtonEvent event; |  | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
|     app->notify_green_off(); |  | ||||||
| } |  | ||||||
| @ -6,7 +6,4 @@ public: | |||||||
|     void on_enter(iButtonApp* app) final; |     void on_enter(iButtonApp* app) final; | ||||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; |     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||||
|     void on_exit(iButtonApp* app) final; |     void on_exit(iButtonApp* app) final; | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void popup_callback(void* context); |  | ||||||
| }; | }; | ||||||
| @ -4,20 +4,19 @@ | |||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| 
 | 
 | ||||||
| #define TAG "FuriHalDelay" | #define TAG "FuriHalDelay" | ||||||
| 
 | uint32_t instructions_per_us; | ||||||
| static uint32_t clk_per_microsecond; |  | ||||||
| 
 | 
 | ||||||
| void furi_hal_delay_init(void) { | void furi_hal_delay_init(void) { | ||||||
|     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; |     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; | ||||||
|     DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; |     DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; | ||||||
|     DWT->CYCCNT = 0U; |     DWT->CYCCNT = 0U; | ||||||
|     clk_per_microsecond = SystemCoreClock / 1000000.0f; |     instructions_per_us = SystemCoreClock / 1000000.0f; | ||||||
|     FURI_LOG_I(TAG, "Init OK"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void delay_us(float microseconds) { | void delay_us(float microseconds) { | ||||||
|     uint32_t start = DWT->CYCCNT; |     uint32_t start = DWT->CYCCNT; | ||||||
|     uint32_t time_ticks = microseconds * clk_per_microsecond; |     uint32_t time_ticks = microseconds * instructions_per_us; | ||||||
|     while((DWT->CYCCNT - start) < time_ticks) { |     while((DWT->CYCCNT - start) < time_ticks) { | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <furi_hal_resources.h> | #include <furi_hal_resources.h> | ||||||
| 
 | 
 | ||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
|  | #include <stm32wbxx_ll_exti.h> | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| @ -91,16 +92,39 @@ void furi_hal_ibutton_emulate_stop() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_ibutton_start() { | void furi_hal_ibutton_start_drive() { | ||||||
|     furi_hal_ibutton_pin_high(); |     furi_hal_ibutton_pin_high(); | ||||||
|     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void furi_hal_ibutton_start_drive_in_isr() { | ||||||
|  |     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||||
|  |     LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_ibutton_start_interrupt() { | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  |     hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_ibutton_start_interrupt_in_isr() { | ||||||
|  |     hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||||
|  |     LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void furi_hal_ibutton_stop() { | void furi_hal_ibutton_stop() { | ||||||
|     furi_hal_ibutton_pin_high(); |     furi_hal_ibutton_pin_high(); | ||||||
|     hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) { | ||||||
|  |     hal_gpio_add_int_callback(&ibutton_gpio, cb, context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_ibutton_remove_interrupt() { | ||||||
|  |     hal_gpio_remove_int_callback(&ibutton_gpio); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void furi_hal_ibutton_pin_low() { | void furi_hal_ibutton_pin_low() { | ||||||
|     hal_gpio_write(&ibutton_gpio, false); |     hal_gpio_write(&ibutton_gpio, false); | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,6 +23,13 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| FuriHalRfid* furi_hal_rfid = NULL; | FuriHalRfid* furi_hal_rfid = NULL; | ||||||
| 
 | 
 | ||||||
|  | #define LFRFID_LL_READ_TIM TIM1 | ||||||
|  | #define LFRFID_LL_READ_CONFIG_CHANNEL LL_TIM_CHANNEL_CH1 | ||||||
|  | #define LFRFID_LL_READ_CHANNEL LL_TIM_CHANNEL_CH1N | ||||||
|  | 
 | ||||||
|  | #define LFRFID_LL_EMULATE_TIM TIM2 | ||||||
|  | #define LFRFID_LL_EMULATE_CHANNEL LL_TIM_CHANNEL_CH3 | ||||||
|  | 
 | ||||||
| void furi_hal_rfid_init() { | void furi_hal_rfid_init() { | ||||||
|     furi_assert(furi_hal_rfid == NULL); |     furi_assert(furi_hal_rfid == NULL); | ||||||
|     furi_hal_rfid = malloc(sizeof(FuriHalRfid)); |     furi_hal_rfid = malloc(sizeof(FuriHalRfid)); | ||||||
| @ -72,7 +79,7 @@ void furi_hal_rfid_pins_reset() { | |||||||
| 
 | 
 | ||||||
| void furi_hal_rfid_pins_emulate() { | void furi_hal_rfid_pins_emulate() { | ||||||
|     // ibutton low
 |     // ibutton low
 | ||||||
|     furi_hal_ibutton_start(); |     furi_hal_ibutton_start_drive(); | ||||||
|     furi_hal_ibutton_pin_low(); |     furi_hal_ibutton_pin_low(); | ||||||
| 
 | 
 | ||||||
|     // pull pin to timer out
 |     // pull pin to timer out
 | ||||||
| @ -89,7 +96,7 @@ void furi_hal_rfid_pins_emulate() { | |||||||
| 
 | 
 | ||||||
| void furi_hal_rfid_pins_read() { | void furi_hal_rfid_pins_read() { | ||||||
|     // ibutton low
 |     // ibutton low
 | ||||||
|     furi_hal_ibutton_start(); |     furi_hal_ibutton_start_drive(); | ||||||
|     furi_hal_ibutton_pin_low(); |     furi_hal_ibutton_pin_low(); | ||||||
| 
 | 
 | ||||||
|     // dont pull rfid antenna
 |     // dont pull rfid antenna
 | ||||||
|  | |||||||
| @ -11,6 +11,8 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | extern uint32_t instructions_per_us; | ||||||
|  | 
 | ||||||
| /** Init DWT
 | /** Init DWT
 | ||||||
|  */ |  */ | ||||||
| void furi_hal_delay_init(void); | void furi_hal_delay_init(void); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include "furi_hal_gpio.h" | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -26,14 +27,60 @@ void furi_hal_ibutton_emulate_set_next(uint32_t period); | |||||||
| 
 | 
 | ||||||
| void furi_hal_ibutton_emulate_stop(); | void furi_hal_ibutton_emulate_stop(); | ||||||
| 
 | 
 | ||||||
| void furi_hal_ibutton_start(); | /**
 | ||||||
|  |  * Sets the pin to normal mode (open collector), and sets it to float | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_start_drive(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt. | ||||||
|  |  * Used in EXTI interrupt context. | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_start_drive_in_isr(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_start_interrupt(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt. | ||||||
|  |  * Used in EXTI interrupt context. | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_start_interrupt_in_isr(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to analog mode, and sets it to float | ||||||
|  |  */ | ||||||
| void furi_hal_ibutton_stop(); | void furi_hal_ibutton_stop(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Attach interrupt callback to iButton pin | ||||||
|  |  * @param cb callback | ||||||
|  |  * @param context context | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Remove interrupt callback from iButton pin | ||||||
|  |  */ | ||||||
|  | void furi_hal_ibutton_remove_interrupt(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to low | ||||||
|  |  */ | ||||||
| void furi_hal_ibutton_pin_low(); | void furi_hal_ibutton_pin_low(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Sets the pin to high (float in iButton pin modes) | ||||||
|  |  */ | ||||||
| void furi_hal_ibutton_pin_high(); | void furi_hal_ibutton_pin_high(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pin level | ||||||
|  |  * @return true if level is high | ||||||
|  |  * @return false if level is low | ||||||
|  |  */ | ||||||
| bool furi_hal_ibutton_pin_get_level(); | bool furi_hal_ibutton_pin_get_level(); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								lib/lib.mk
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								lib/lib.mk
									
									
									
									
									
								
							| @ -65,16 +65,6 @@ CFLAGS			+= -I$(LIB_DIR)/app_scene_template | |||||||
| CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash | CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash | ||||||
| C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | ||||||
| 
 | 
 | ||||||
| # onewire library
 |  | ||||||
| ONEWIRE_DIR		= $(LIB_DIR)/onewire |  | ||||||
| CFLAGS			+= -I$(ONEWIRE_DIR) |  | ||||||
| CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp) |  | ||||||
| 
 |  | ||||||
| # cyfral library
 |  | ||||||
| CYFRAL_DIR		= $(LIB_DIR)/cyfral |  | ||||||
| CFLAGS			+= -I$(CYFRAL_DIR) |  | ||||||
| CPP_SOURCES		+= $(wildcard $(CYFRAL_DIR)/*.cpp) |  | ||||||
| 
 |  | ||||||
| # common apps api
 | # common apps api
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/common-api | CFLAGS			+= -I$(LIB_DIR)/common-api | ||||||
| 
 | 
 | ||||||
| @ -128,3 +118,8 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/flipper_format/*.c) | |||||||
| # Micro-ECC
 | # Micro-ECC
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/micro-ecc | CFLAGS			+= -I$(LIB_DIR)/micro-ecc | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/micro-ecc/*.c) | C_SOURCES		+= $(wildcard $(LIB_DIR)/micro-ecc/*.c) | ||||||
|  | 
 | ||||||
|  | # iButton and OneWire
 | ||||||
|  | C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*.c) | ||||||
|  | C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*/*.c) | ||||||
|  | C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*/*/*.c) | ||||||
|  | |||||||
							
								
								
									
										126
									
								
								lib/one_wire/ibutton/encoder/encoder_cyfral.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								lib/one_wire/ibutton/encoder/encoder_cyfral.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | #include "encoder_cyfral.h" | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #define CYFRAL_DATA_SIZE sizeof(uint16_t) | ||||||
|  | #define CYFRAL_PERIOD (125 * instructions_per_us) | ||||||
|  | #define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) | ||||||
|  | #define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) | ||||||
|  | #define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) | ||||||
|  | #define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) | ||||||
|  | 
 | ||||||
|  | #define CYFRAL_SET_DATA(level, len) \ | ||||||
|  |     *polarity = level;              \ | ||||||
|  |     *length = len; | ||||||
|  | 
 | ||||||
|  | struct EncoderCyfral { | ||||||
|  |     uint32_t data; | ||||||
|  |     uint32_t index; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | EncoderCyfral* encoder_cyfral_alloc() { | ||||||
|  |     EncoderCyfral* cyfral = malloc(sizeof(EncoderCyfral)); | ||||||
|  |     encoder_cyfral_reset(cyfral); | ||||||
|  |     return cyfral; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_cyfral_free(EncoderCyfral* cyfral) { | ||||||
|  |     free(cyfral); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_cyfral_reset(EncoderCyfral* cyfral) { | ||||||
|  |     cyfral->data = 0; | ||||||
|  |     cyfral->index = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t cyfral_encoder_encode(const uint16_t data) { | ||||||
|  |     uint32_t value = 0; | ||||||
|  |     for(int8_t i = 0; i <= 7; i++) { | ||||||
|  |         switch((data >> (i * 2)) & 0b00000011) { | ||||||
|  |         case 0b11: | ||||||
|  |             value = value << 4; | ||||||
|  |             value += 0b00000111; | ||||||
|  |             break; | ||||||
|  |         case 0b10: | ||||||
|  |             value = value << 4; | ||||||
|  |             value += 0b00001011; | ||||||
|  |             break; | ||||||
|  |         case 0b01: | ||||||
|  |             value = value << 4; | ||||||
|  |             value += 0b00001101; | ||||||
|  |             break; | ||||||
|  |         case 0b00: | ||||||
|  |             value = value << 4; | ||||||
|  |             value += 0b00001110; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size) { | ||||||
|  |     furi_assert(cyfral); | ||||||
|  |     furi_check(data_size >= CYFRAL_DATA_SIZE); | ||||||
|  |     uint16_t intermediate; | ||||||
|  |     memcpy(&intermediate, data, CYFRAL_DATA_SIZE); | ||||||
|  |     cyfral->data = cyfral_encoder_encode(intermediate); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length) { | ||||||
|  |     if(cyfral->index < 8) { | ||||||
|  |         // start word (0b0001)
 | ||||||
|  |         switch(cyfral->index) { | ||||||
|  |         case 0: | ||||||
|  |             CYFRAL_SET_DATA(false, CYFRAL_0_LOW); | ||||||
|  |             break; | ||||||
|  |         case 1: | ||||||
|  |             CYFRAL_SET_DATA(true, CYFRAL_0_HI); | ||||||
|  |             break; | ||||||
|  |         case 2: | ||||||
|  |             CYFRAL_SET_DATA(false, CYFRAL_0_LOW); | ||||||
|  |             break; | ||||||
|  |         case 3: | ||||||
|  |             CYFRAL_SET_DATA(true, CYFRAL_0_HI); | ||||||
|  |             break; | ||||||
|  |         case 4: | ||||||
|  |             CYFRAL_SET_DATA(false, CYFRAL_0_LOW); | ||||||
|  |             break; | ||||||
|  |         case 5: | ||||||
|  |             CYFRAL_SET_DATA(true, CYFRAL_0_HI); | ||||||
|  |             break; | ||||||
|  |         case 6: | ||||||
|  |             CYFRAL_SET_DATA(false, CYFRAL_1_LOW); | ||||||
|  |             break; | ||||||
|  |         case 7: | ||||||
|  |             CYFRAL_SET_DATA(true, CYFRAL_1_HI); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // data
 | ||||||
|  |         uint8_t data_start_index = cyfral->index - 8; | ||||||
|  |         bool clock_polarity = (data_start_index) % 2; | ||||||
|  |         uint8_t bit_index = (data_start_index) / 2; | ||||||
|  |         bool bit_value = ((cyfral->data >> bit_index) & 1); | ||||||
|  | 
 | ||||||
|  |         if(!clock_polarity) { | ||||||
|  |             if(bit_value) { | ||||||
|  |                 CYFRAL_SET_DATA(false, CYFRAL_1_LOW); | ||||||
|  |             } else { | ||||||
|  |                 CYFRAL_SET_DATA(false, CYFRAL_0_LOW); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(bit_value) { | ||||||
|  |                 CYFRAL_SET_DATA(true, CYFRAL_1_HI); | ||||||
|  |             } else { | ||||||
|  |                 CYFRAL_SET_DATA(true, CYFRAL_0_HI); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cyfral->index++; | ||||||
|  |     if(cyfral->index >= (9 * 4 * 2)) { | ||||||
|  |         cyfral->index = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								lib/one_wire/ibutton/encoder/encoder_cyfral.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								lib/one_wire/ibutton/encoder/encoder_cyfral.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file encoder_cyfral.h | ||||||
|  |  *  | ||||||
|  |  * Cyfral pulse format encoder | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct EncoderCyfral EncoderCyfral; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate Cyfral encoder | ||||||
|  |  * @return EncoderCyfral*  | ||||||
|  |  */ | ||||||
|  | EncoderCyfral* encoder_cyfral_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate Cyfral encoder | ||||||
|  |  * @param cyfral  | ||||||
|  |  */ | ||||||
|  | void encoder_cyfral_free(EncoderCyfral* cyfral); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset Cyfral encoder | ||||||
|  |  * @param cyfral  | ||||||
|  |  */ | ||||||
|  | void encoder_cyfral_reset(EncoderCyfral* cyfral); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set data to be encoded to Cyfral pulse format, 2 bytes | ||||||
|  |  * @param cyfral  | ||||||
|  |  * @param data  | ||||||
|  |  * @param data_size | ||||||
|  |  */ | ||||||
|  | void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Pop pulse from Cyfral encoder | ||||||
|  |  * @param cyfral  | ||||||
|  |  * @param polarity  | ||||||
|  |  * @param length  | ||||||
|  |  */ | ||||||
|  | void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										93
									
								
								lib/one_wire/ibutton/encoder/encoder_metakom.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								lib/one_wire/ibutton/encoder/encoder_metakom.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | #include "encoder_metakom.h" | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #define METAKOM_DATA_SIZE sizeof(uint32_t) | ||||||
|  | #define METAKOM_PERIOD (125 * instructions_per_us) | ||||||
|  | #define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f) | ||||||
|  | #define METAKOM_0_HI (METAKOM_PERIOD * 0.66f) | ||||||
|  | #define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f) | ||||||
|  | #define METAKOM_1_HI (METAKOM_PERIOD * 0.33f) | ||||||
|  | 
 | ||||||
|  | #define METAKOM_SET_DATA(level, len) \ | ||||||
|  |     *polarity = !level;              \ | ||||||
|  |     *length = len; | ||||||
|  | 
 | ||||||
|  | struct EncoderMetakom { | ||||||
|  |     uint32_t data; | ||||||
|  |     uint32_t index; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | EncoderMetakom* encoder_metakom_alloc() { | ||||||
|  |     EncoderMetakom* metakom = malloc(sizeof(EncoderMetakom)); | ||||||
|  |     encoder_metakom_reset(metakom); | ||||||
|  |     return metakom; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_metakom_free(EncoderMetakom* metakom) { | ||||||
|  |     free(metakom); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_metakom_reset(EncoderMetakom* metakom) { | ||||||
|  |     metakom->data = 0; | ||||||
|  |     metakom->index = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size) { | ||||||
|  |     furi_assert(metakom); | ||||||
|  |     furi_check(data_size >= METAKOM_DATA_SIZE); | ||||||
|  |     memcpy(&metakom->data, data, METAKOM_DATA_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length) { | ||||||
|  |     if(metakom->index == 0) { | ||||||
|  |         // sync bit
 | ||||||
|  |         METAKOM_SET_DATA(true, METAKOM_PERIOD); | ||||||
|  |     } else if(metakom->index >= 1 && metakom->index <= 6) { | ||||||
|  |         // start word (0b010)
 | ||||||
|  |         switch(metakom->index) { | ||||||
|  |         case 1: | ||||||
|  |             METAKOM_SET_DATA(false, METAKOM_0_LOW); | ||||||
|  |             break; | ||||||
|  |         case 2: | ||||||
|  |             METAKOM_SET_DATA(true, METAKOM_0_HI); | ||||||
|  |             break; | ||||||
|  |         case 3: | ||||||
|  |             METAKOM_SET_DATA(false, METAKOM_1_LOW); | ||||||
|  |             break; | ||||||
|  |         case 4: | ||||||
|  |             METAKOM_SET_DATA(true, METAKOM_1_HI); | ||||||
|  |             break; | ||||||
|  |         case 5: | ||||||
|  |             METAKOM_SET_DATA(false, METAKOM_0_LOW); | ||||||
|  |             break; | ||||||
|  |         case 6: | ||||||
|  |             METAKOM_SET_DATA(true, METAKOM_0_HI); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // data
 | ||||||
|  |         uint8_t data_start_index = metakom->index - 7; | ||||||
|  |         bool clock_polarity = (data_start_index) % 2; | ||||||
|  |         uint8_t bit_index = (data_start_index) / 2; | ||||||
|  |         bool bit_value = (metakom->data >> (32 - 1 - bit_index)) & 1; | ||||||
|  | 
 | ||||||
|  |         if(!clock_polarity) { | ||||||
|  |             if(bit_value) { | ||||||
|  |                 METAKOM_SET_DATA(false, METAKOM_1_LOW); | ||||||
|  |             } else { | ||||||
|  |                 METAKOM_SET_DATA(false, METAKOM_0_LOW); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(bit_value) { | ||||||
|  |                 METAKOM_SET_DATA(true, METAKOM_1_HI); | ||||||
|  |             } else { | ||||||
|  |                 METAKOM_SET_DATA(true, METAKOM_0_HI); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     metakom->index++; | ||||||
|  |     if(metakom->index >= (1 + 3 * 2 + 32 * 2)) { | ||||||
|  |         metakom->index = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								lib/one_wire/ibutton/encoder/encoder_metakom.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								lib/one_wire/ibutton/encoder/encoder_metakom.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file encoder_metakom.h | ||||||
|  |  *  | ||||||
|  |  * Metakom pulse format encoder | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct EncoderMetakom EncoderMetakom; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate Metakom encoder | ||||||
|  |  * @return EncoderMetakom*  | ||||||
|  |  */ | ||||||
|  | EncoderMetakom* encoder_metakom_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate Metakom encoder | ||||||
|  |  * @param metakom  | ||||||
|  |  */ | ||||||
|  | void encoder_metakom_free(EncoderMetakom* metakom); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset Metakom encoder | ||||||
|  |  * @param metakom  | ||||||
|  |  */ | ||||||
|  | void encoder_metakom_reset(EncoderMetakom* metakom); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set data to be encoded to Metakom pulse format, 4 bytes | ||||||
|  |  * @param metakom  | ||||||
|  |  * @param data  | ||||||
|  |  * @param data_size  | ||||||
|  |  */ | ||||||
|  | void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Pop pulse from Metakom encoder | ||||||
|  |  * @param cyfral  | ||||||
|  |  * @param polarity  | ||||||
|  |  * @param length  | ||||||
|  |  */ | ||||||
|  | void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										121
									
								
								lib/one_wire/ibutton/ibutton_key.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								lib/one_wire/ibutton/ibutton_key.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <one_wire/maxim_crc.h> | ||||||
|  | #include "ibutton_key.h" | ||||||
|  | 
 | ||||||
|  | struct iButtonKey { | ||||||
|  |     uint8_t data[IBUTTON_KEY_DATA_SIZE]; | ||||||
|  |     char name[IBUTTON_KEY_NAME_SIZE]; | ||||||
|  |     iButtonKeyType type; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | iButtonKey* ibutton_key_alloc() { | ||||||
|  |     iButtonKey* key = malloc(sizeof(iButtonKey)); | ||||||
|  |     memset(key, 0, sizeof(iButtonKey)); | ||||||
|  |     return key; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_free(iButtonKey* key) { | ||||||
|  |     free(key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_set(iButtonKey* to, const iButtonKey* from) { | ||||||
|  |     memcpy(to, from, sizeof(iButtonKey)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count) { | ||||||
|  |     furi_check(data_count > 0); | ||||||
|  |     furi_check(data_count <= IBUTTON_KEY_DATA_SIZE); | ||||||
|  | 
 | ||||||
|  |     memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); | ||||||
|  |     memcpy(key->data, data, data_count); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_clear_data(iButtonKey* key) { | ||||||
|  |     memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const uint8_t* ibutton_key_get_data_p(iButtonKey* key) { | ||||||
|  |     return key->data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ibutton_key_get_data_size(iButtonKey* key) { | ||||||
|  |     return ibutton_key_get_size_by_type(key->type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_set_name(iButtonKey* key, const char* name) { | ||||||
|  |     strlcpy(key->name, name, IBUTTON_KEY_NAME_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* ibutton_key_get_name_p(iButtonKey* key) { | ||||||
|  |     return key->name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) { | ||||||
|  |     key->type = key_type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonKeyType ibutton_key_get_type(iButtonKey* key) { | ||||||
|  |     return key->type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) { | ||||||
|  |     switch(key_type) { | ||||||
|  |     case iButtonKeyCyfral: | ||||||
|  |         return "Cyfral"; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyMetakom: | ||||||
|  |         return "Metakom"; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyDS1990: | ||||||
|  |         return "Dallas"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_crash("Invalid iButton type"); | ||||||
|  |         return ""; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type) { | ||||||
|  |     if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyCyfral)) == 0) { | ||||||
|  |         *key_type = iButtonKeyCyfral; | ||||||
|  |     } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyMetakom)) == 0) { | ||||||
|  |         *key_type = iButtonKeyMetakom; | ||||||
|  |     } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyDS1990)) == 0) { | ||||||
|  |         *key_type = iButtonKeyDS1990; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type) { | ||||||
|  |     uint8_t size = 0; | ||||||
|  | 
 | ||||||
|  |     switch(key_type) { | ||||||
|  |     case iButtonKeyCyfral: | ||||||
|  |         size = 2; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyMetakom: | ||||||
|  |         size = 4; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyDS1990: | ||||||
|  |         size = 8; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t ibutton_key_get_max_size() { | ||||||
|  |     return IBUTTON_KEY_DATA_SIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_key_dallas_crc_is_valid(iButtonKey* key) { | ||||||
|  |     return (maxim_crc8(key->data, 8, MAXIM_CRC8_INIT) == 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_key_dallas_is_1990_key(iButtonKey* key) { | ||||||
|  |     return (key->data[0] == 0x01); | ||||||
|  | } | ||||||
							
								
								
									
										145
									
								
								lib/one_wire/ibutton/ibutton_key.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lib/one_wire/ibutton/ibutton_key.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file ibutton_key.h | ||||||
|  |  *  | ||||||
|  |  * iButton key data holder | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define IBUTTON_KEY_DATA_SIZE 8 | ||||||
|  | #define IBUTTON_KEY_NAME_SIZE 22 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonKeyDS1990, | ||||||
|  |     iButtonKeyCyfral, | ||||||
|  |     iButtonKeyMetakom, | ||||||
|  | } iButtonKeyType; | ||||||
|  | 
 | ||||||
|  | typedef struct iButtonKey iButtonKey; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate key | ||||||
|  |  * @return iButtonKey*  | ||||||
|  |  */ | ||||||
|  | iButtonKey* ibutton_key_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free key | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_free(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Copy key | ||||||
|  |  * @param to  | ||||||
|  |  * @param from  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_set(iButtonKey* to, const iButtonKey* from); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set key data | ||||||
|  |  * @param key  | ||||||
|  |  * @param data  | ||||||
|  |  * @param data_count  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Clear key data | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_clear_data(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pointer to key data | ||||||
|  |  * @param key  | ||||||
|  |  * @return const uint8_t*  | ||||||
|  |  */ | ||||||
|  | const uint8_t* ibutton_key_get_data_p(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get key data size | ||||||
|  |  * @param key  | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t ibutton_key_get_data_size(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set key name | ||||||
|  |  * @param key  | ||||||
|  |  * @param name  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_set_name(iButtonKey* key, const char* name); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pointer to key name | ||||||
|  |  * @param key  | ||||||
|  |  * @return const char*  | ||||||
|  |  */ | ||||||
|  | const char* ibutton_key_get_name_p(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set key type | ||||||
|  |  * @param key  | ||||||
|  |  * @param key_type  | ||||||
|  |  */ | ||||||
|  | void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get key type | ||||||
|  |  * @param key  | ||||||
|  |  * @return iButtonKeyType  | ||||||
|  |  */ | ||||||
|  | iButtonKeyType ibutton_key_get_type(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get type string from key type | ||||||
|  |  * @param key_type  | ||||||
|  |  * @return const char*  | ||||||
|  |  */ | ||||||
|  | const char* ibutton_key_get_string_by_type(iButtonKeyType key_type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get key type from string | ||||||
|  |  * @param type_string  | ||||||
|  |  * @param key_type  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get key data size from type | ||||||
|  |  * @param key_type  | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get max key size | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t ibutton_key_get_max_size(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Check if CRC for onewire key is valid | ||||||
|  |  * @param key  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | bool ibutton_key_dallas_crc_is_valid(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Check if onewire key is a DS1990 key | ||||||
|  |  * @param key  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | bool ibutton_key_dallas_is_1990_key(iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										28
									
								
								lib/one_wire/ibutton/ibutton_key_command.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/one_wire/ibutton/ibutton_key_command.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file ibutton_key_command.h | ||||||
|  |  *  | ||||||
|  |  * List of misc commands for Dallas and blanks | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1 | ||||||
|  | #define RW1990_1_CMD_READ_RECORD_FLAG 0xB5 | ||||||
|  | #define RW1990_1_CMD_WRITE_ROM 0xD5 | ||||||
|  | 
 | ||||||
|  | #define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D | ||||||
|  | #define RW1990_2_CMD_READ_RECORD_FLAG 0x1E | ||||||
|  | #define RW1990_2_CMD_WRITE_ROM 0xD5 | ||||||
|  | 
 | ||||||
|  | #define TM2004_CMD_READ_STATUS 0xAA | ||||||
|  | #define TM2004_CMD_READ_MEMORY 0xF0 | ||||||
|  | #define TM2004_CMD_WRITE_ROM 0x3C | ||||||
|  | #define TM2004_CMD_FINALIZATION 0x35 | ||||||
|  | #define TM2004_ANSWER_READ_MEMORY 0xF5 | ||||||
|  | 
 | ||||||
|  | #define TM01_CMD_WRITE_RECORD_FLAG 0xC1 | ||||||
|  | #define TM01_CMD_WRITE_ROM 0xC5 | ||||||
|  | #define TM01_CMD_SWITCH_TO_CYFRAL 0xCA | ||||||
|  | #define TM01_CMD_SWITCH_TO_METAKOM 0xCB | ||||||
|  | 
 | ||||||
|  | #define DS1990_CMD_READ_ROM 0x33 | ||||||
							
								
								
									
										197
									
								
								lib/one_wire/ibutton/ibutton_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								lib/one_wire/ibutton/ibutton_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,197 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <atomic.h> | ||||||
|  | #include "ibutton_worker_i.h" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonMessageEnd, | ||||||
|  |     iButtonMessageStop, | ||||||
|  |     iButtonMessageRead, | ||||||
|  |     iButtonMessageWrite, | ||||||
|  |     iButtonMessageEmulate, | ||||||
|  | } iButtonMessageType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     iButtonMessageType type; | ||||||
|  |     union { | ||||||
|  |         iButtonKey* key; | ||||||
|  |     } data; | ||||||
|  | } iButtonMessage; | ||||||
|  | 
 | ||||||
|  | static int32_t ibutton_worker_thread(void* thread_context); | ||||||
|  | 
 | ||||||
|  | iButtonWorker* ibutton_worker_alloc() { | ||||||
|  |     iButtonWorker* worker = malloc(sizeof(iButtonWorker)); | ||||||
|  |     worker->key_p = NULL; | ||||||
|  |     worker->key_data = malloc(ibutton_key_get_max_size()); | ||||||
|  |     worker->host = onewire_host_alloc(); | ||||||
|  |     worker->slave = onewire_slave_alloc(); | ||||||
|  |     worker->writer = ibutton_writer_alloc(worker->host); | ||||||
|  |     worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); | ||||||
|  |     worker->pulse_decoder = pulse_decoder_alloc(); | ||||||
|  |     worker->protocol_cyfral = protocol_cyfral_alloc(); | ||||||
|  |     worker->protocol_metakom = protocol_metakom_alloc(); | ||||||
|  |     worker->messages = osMessageQueueNew(1, sizeof(iButtonMessage), NULL); | ||||||
|  |     worker->mode_index = iButtonWorkerIdle; | ||||||
|  |     worker->last_dwt_value = 0; | ||||||
|  |     worker->read_cb = NULL; | ||||||
|  |     worker->write_cb = NULL; | ||||||
|  |     worker->emulate_cb = NULL; | ||||||
|  |     worker->cb_ctx = NULL; | ||||||
|  | 
 | ||||||
|  |     worker->encoder_cyfral = encoder_cyfral_alloc(); | ||||||
|  |     worker->encoder_metakom = encoder_metakom_alloc(); | ||||||
|  | 
 | ||||||
|  |     worker->thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(worker->thread, "ibutton_worker"); | ||||||
|  |     furi_thread_set_callback(worker->thread, ibutton_worker_thread); | ||||||
|  |     furi_thread_set_context(worker->thread, worker); | ||||||
|  |     furi_thread_set_stack_size(worker->thread, 2048); | ||||||
|  | 
 | ||||||
|  |     pulse_decoder_add_protocol( | ||||||
|  |         worker->pulse_decoder, | ||||||
|  |         protocol_cyfral_get_protocol(worker->protocol_cyfral), | ||||||
|  |         PulseProtocolCyfral); | ||||||
|  |     pulse_decoder_add_protocol( | ||||||
|  |         worker->pulse_decoder, | ||||||
|  |         protocol_metakom_get_protocol(worker->protocol_metakom), | ||||||
|  |         PulseProtocolMetakom); | ||||||
|  | 
 | ||||||
|  |     return worker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_read_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerReadCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_check(worker->mode_index == iButtonWorkerIdle); | ||||||
|  |     worker->read_cb = callback; | ||||||
|  |     worker->cb_ctx = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_write_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerWriteCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_check(worker->mode_index == iButtonWorkerIdle); | ||||||
|  |     worker->write_cb = callback; | ||||||
|  |     worker->cb_ctx = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerEmulateCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_check(worker->mode_index == iButtonWorkerIdle); | ||||||
|  |     worker->emulate_cb = callback; | ||||||
|  |     worker->cb_ctx = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) { | ||||||
|  |     iButtonMessage message = {.type = iButtonMessageRead, .data.key = key}; | ||||||
|  |     furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key) { | ||||||
|  |     iButtonMessage message = {.type = iButtonMessageWrite, .data.key = key}; | ||||||
|  |     furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key) { | ||||||
|  |     iButtonMessage message = {.type = iButtonMessageEmulate, .data.key = key}; | ||||||
|  |     furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_stop(iButtonWorker* worker) { | ||||||
|  |     iButtonMessage message = {.type = iButtonMessageStop}; | ||||||
|  |     furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_free(iButtonWorker* worker) { | ||||||
|  |     pulse_decoder_free(worker->pulse_decoder); | ||||||
|  |     protocol_metakom_free(worker->protocol_metakom); | ||||||
|  |     protocol_cyfral_free(worker->protocol_cyfral); | ||||||
|  | 
 | ||||||
|  |     ibutton_writer_free(worker->writer); | ||||||
|  | 
 | ||||||
|  |     onewire_slave_free(worker->slave); | ||||||
|  | 
 | ||||||
|  |     onewire_host_free(worker->host); | ||||||
|  |     onewire_device_free(worker->device); | ||||||
|  | 
 | ||||||
|  |     encoder_cyfral_free(worker->encoder_cyfral); | ||||||
|  |     encoder_metakom_free(worker->encoder_metakom); | ||||||
|  | 
 | ||||||
|  |     osMessageQueueDelete(worker->messages); | ||||||
|  | 
 | ||||||
|  |     furi_thread_free(worker->thread); | ||||||
|  |     free(worker->key_data); | ||||||
|  |     free(worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_start_thread(iButtonWorker* worker) { | ||||||
|  |     furi_thread_start(worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_stop_thread(iButtonWorker* worker) { | ||||||
|  |     iButtonMessage message = {.type = iButtonMessageEnd}; | ||||||
|  |     furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK); | ||||||
|  |     furi_thread_join(worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) { | ||||||
|  |     ibutton_worker_modes[worker->mode_index].stop(worker); | ||||||
|  |     worker->mode_index = mode; | ||||||
|  |     ibutton_worker_modes[worker->mode_index].start(worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { | ||||||
|  |     worker->key_p = key; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t ibutton_worker_thread(void* thread_context) { | ||||||
|  |     iButtonWorker* worker = thread_context; | ||||||
|  |     bool running = true; | ||||||
|  |     iButtonMessage message; | ||||||
|  |     osStatus_t status; | ||||||
|  | 
 | ||||||
|  |     ibutton_worker_modes[worker->mode_index].start(worker); | ||||||
|  | 
 | ||||||
|  |     while(running) { | ||||||
|  |         status = osMessageQueueGet( | ||||||
|  |             worker->messages, &message, NULL, ibutton_worker_modes[worker->mode_index].quant); | ||||||
|  |         if(status == osOK) { | ||||||
|  |             switch(message.type) { | ||||||
|  |             case iButtonMessageEnd: | ||||||
|  |                 ibutton_worker_switch_mode(worker, iButtonWorkerIdle); | ||||||
|  |                 ibutton_worker_set_key_p(worker, NULL); | ||||||
|  |                 running = false; | ||||||
|  |                 break; | ||||||
|  |             case iButtonMessageStop: | ||||||
|  |                 ibutton_worker_switch_mode(worker, iButtonWorkerIdle); | ||||||
|  |                 ibutton_worker_set_key_p(worker, NULL); | ||||||
|  |                 break; | ||||||
|  |             case iButtonMessageRead: | ||||||
|  |                 ibutton_worker_set_key_p(worker, message.data.key); | ||||||
|  |                 ibutton_worker_switch_mode(worker, iButtonWorkerRead); | ||||||
|  |                 break; | ||||||
|  |             case iButtonMessageWrite: | ||||||
|  |                 ibutton_worker_set_key_p(worker, message.data.key); | ||||||
|  |                 ibutton_worker_switch_mode(worker, iButtonWorkerWrite); | ||||||
|  |                 break; | ||||||
|  |             case iButtonMessageEmulate: | ||||||
|  |                 ibutton_worker_set_key_p(worker, message.data.key); | ||||||
|  |                 ibutton_worker_switch_mode(worker, iButtonWorkerEmulate); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if(status == osErrorTimeout) { | ||||||
|  |             ibutton_worker_modes[worker->mode_index].tick(worker); | ||||||
|  |         } else { | ||||||
|  |             furi_crash("iButton worker error"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ibutton_worker_modes[worker->mode_index].stop(worker); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								lib/one_wire/ibutton/ibutton_worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								lib/one_wire/ibutton/ibutton_worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file ibutton_worker.h | ||||||
|  |  *  | ||||||
|  |  * iButton worker | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "ibutton_key.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonWorkerWriteOK, | ||||||
|  |     iButtonWorkerWriteSameKey, | ||||||
|  |     iButtonWorkerWriteNoDetect, | ||||||
|  |     iButtonWorkerWriteCannotWrite, | ||||||
|  | } iButtonWorkerWriteResult; | ||||||
|  | 
 | ||||||
|  | typedef void (*iButtonWorkerReadCallback)(void* context); | ||||||
|  | typedef void (*iButtonWorkerWriteCallback)(void* context, iButtonWorkerWriteResult result); | ||||||
|  | typedef void (*iButtonWorkerEmulateCallback)(void* context, bool emulated); | ||||||
|  | 
 | ||||||
|  | typedef struct iButtonWorker iButtonWorker; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate ibutton worker | ||||||
|  |  * @return iButtonWorker*  | ||||||
|  |  */ | ||||||
|  | iButtonWorker* ibutton_worker_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free ibutton worker | ||||||
|  |  * @param worker  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_free(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start ibutton worker thread | ||||||
|  |  * @param worker  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_start_thread(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop ibutton worker thread | ||||||
|  |  * @param worker  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_stop_thread(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "read success" callback | ||||||
|  |  * @param worker  | ||||||
|  |  * @param callback  | ||||||
|  |  * @param context  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_read_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerReadCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start read mode | ||||||
|  |  * @param worker  | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "write event" callback | ||||||
|  |  * @param worker  | ||||||
|  |  * @param callback  | ||||||
|  |  * @param context  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_write_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerWriteCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start write mode | ||||||
|  |  * @param worker  | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "emulate success" callback | ||||||
|  |  * @param worker  | ||||||
|  |  * @param callback  | ||||||
|  |  * @param context  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_emulate_set_callback( | ||||||
|  |     iButtonWorker* worker, | ||||||
|  |     iButtonWorkerEmulateCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start emulate mode | ||||||
|  |  * @param worker  | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop all modes | ||||||
|  |  * @param worker  | ||||||
|  |  */ | ||||||
|  | void ibutton_worker_stop(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										79
									
								
								lib/one_wire/ibutton/ibutton_worker_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								lib/one_wire/ibutton/ibutton_worker_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file ibutton_worker_i.h | ||||||
|  |  *  | ||||||
|  |  * iButton worker, internal definitions  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "ibutton_worker.h" | ||||||
|  | #include "ibutton_writer.h" | ||||||
|  | #include "../one_wire_host.h" | ||||||
|  | #include "../one_wire_slave.h" | ||||||
|  | #include "../one_wire_device.h" | ||||||
|  | #include "../pulse_protocols/pulse_decoder.h" | ||||||
|  | #include "pulse_protocols/protocol_cyfral.h" | ||||||
|  | #include "pulse_protocols/protocol_metakom.h" | ||||||
|  | #include "encoder/encoder_cyfral.h" | ||||||
|  | #include "encoder/encoder_metakom.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PulseProtocolCyfral, | ||||||
|  |     PulseProtocolMetakom, | ||||||
|  | } PulseProtocols; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const uint32_t quant; | ||||||
|  |     const void (*start)(iButtonWorker* worker); | ||||||
|  |     const void (*tick)(iButtonWorker* worker); | ||||||
|  |     const void (*stop)(iButtonWorker* worker); | ||||||
|  | } iButtonWorkerModeType; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonWorkerIdle = 0, | ||||||
|  |     iButtonWorkerRead = 1, | ||||||
|  |     iButtonWorkerWrite = 2, | ||||||
|  |     iButtonWorkerEmulate = 3, | ||||||
|  | } iButtonWorkerMode; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonEmulateModeCyfral, | ||||||
|  |     iButtonEmulateModeMetakom, | ||||||
|  | } iButtonEmulateMode; | ||||||
|  | 
 | ||||||
|  | struct iButtonWorker { | ||||||
|  |     iButtonKey* key_p; | ||||||
|  |     uint8_t* key_data; | ||||||
|  |     OneWireHost* host; | ||||||
|  |     OneWireSlave* slave; | ||||||
|  |     OneWireDevice* device; | ||||||
|  |     iButtonWriter* writer; | ||||||
|  |     iButtonWorkerMode mode_index; | ||||||
|  |     osMessageQueueId_t messages; | ||||||
|  |     FuriThread* thread; | ||||||
|  | 
 | ||||||
|  |     PulseDecoder* pulse_decoder; | ||||||
|  |     ProtocolCyfral* protocol_cyfral; | ||||||
|  |     ProtocolMetakom* protocol_metakom; | ||||||
|  |     uint32_t last_dwt_value; | ||||||
|  | 
 | ||||||
|  |     iButtonWorkerReadCallback read_cb; | ||||||
|  |     iButtonWorkerWriteCallback write_cb; | ||||||
|  |     iButtonWorkerEmulateCallback emulate_cb; | ||||||
|  |     void* cb_ctx; | ||||||
|  | 
 | ||||||
|  |     EncoderCyfral* encoder_cyfral; | ||||||
|  |     EncoderMetakom* encoder_metakom; | ||||||
|  |     iButtonEmulateMode emulate_mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern const iButtonWorkerModeType ibutton_worker_modes[]; | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										330
									
								
								lib/one_wire/ibutton/ibutton_worker_modes.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								lib/one_wire/ibutton/ibutton_worker_modes.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,330 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include "ibutton_worker_i.h" | ||||||
|  | #include "ibutton_key_command.h" | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_idle_start(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_idle_tick(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_idle_stop(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_emulate_start(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_read_start(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_read_tick(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_read_stop(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_write_start(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_write_tick(iButtonWorker* worker); | ||||||
|  | void ibutton_worker_mode_write_stop(iButtonWorker* worker); | ||||||
|  | 
 | ||||||
|  | const iButtonWorkerModeType ibutton_worker_modes[] = { | ||||||
|  |     { | ||||||
|  |         .quant = osWaitForever, | ||||||
|  |         .start = ibutton_worker_mode_idle_start, | ||||||
|  |         .tick = ibutton_worker_mode_idle_tick, | ||||||
|  |         .stop = ibutton_worker_mode_idle_stop, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .quant = 100, | ||||||
|  |         .start = ibutton_worker_mode_read_start, | ||||||
|  |         .tick = ibutton_worker_mode_read_tick, | ||||||
|  |         .stop = ibutton_worker_mode_read_stop, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .quant = 1000, | ||||||
|  |         .start = ibutton_worker_mode_write_start, | ||||||
|  |         .tick = ibutton_worker_mode_write_tick, | ||||||
|  |         .stop = ibutton_worker_mode_write_stop, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .quant = 1000, | ||||||
|  |         .start = ibutton_worker_mode_emulate_start, | ||||||
|  |         .tick = ibutton_worker_mode_emulate_tick, | ||||||
|  |         .stop = ibutton_worker_mode_emulate_stop, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*********************** IDLE ***********************/ | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_idle_start(iButtonWorker* worker) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_idle_tick(iButtonWorker* worker) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*********************** READ ***********************/ | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_comparator_callback(bool level, void* context) { | ||||||
|  |     iButtonWorker* worker = context; | ||||||
|  | 
 | ||||||
|  |     uint32_t current_dwt_value = DWT->CYCCNT; | ||||||
|  | 
 | ||||||
|  |     pulse_decoder_process_pulse( | ||||||
|  |         worker->pulse_decoder, level, current_dwt_value - worker->last_dwt_value); | ||||||
|  | 
 | ||||||
|  |     worker->last_dwt_value = current_dwt_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_worker_read_comparator(iButtonWorker* worker) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     pulse_decoder_reset(worker->pulse_decoder); | ||||||
|  | 
 | ||||||
|  |     furi_hal_rfid_pins_reset(); | ||||||
|  |     // pulldown pull pin, we sense the signal through the analog part of the RFID schematic
 | ||||||
|  |     furi_hal_rfid_pin_pull_pulldown(); | ||||||
|  |     furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, worker); | ||||||
|  |     worker->last_dwt_value = DWT->CYCCNT; | ||||||
|  |     furi_hal_rfid_comp_start(); | ||||||
|  | 
 | ||||||
|  |     // TODO: rework with thread events, "pulse_decoder_get_decoded_index_with_timeout"
 | ||||||
|  |     delay(100); | ||||||
|  |     int32_t decoded_index = pulse_decoder_get_decoded_index(worker->pulse_decoder); | ||||||
|  |     if(decoded_index >= 0) { | ||||||
|  |         pulse_decoder_get_data( | ||||||
|  |             worker->pulse_decoder, decoded_index, worker->key_data, ibutton_key_get_max_size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(decoded_index) { | ||||||
|  |     case PulseProtocolCyfral: | ||||||
|  |         furi_check(worker->key_p != NULL); | ||||||
|  |         ibutton_key_set_type(worker->key_p, iButtonKeyCyfral); | ||||||
|  |         ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); | ||||||
|  |         result = true; | ||||||
|  |         break; | ||||||
|  |     case PulseProtocolMetakom: | ||||||
|  |         furi_check(worker->key_p != NULL); | ||||||
|  |         ibutton_key_set_type(worker->key_p, iButtonKeyMetakom); | ||||||
|  |         ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); | ||||||
|  |         result = true; | ||||||
|  |         break; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_rfid_comp_stop(); | ||||||
|  |     furi_hal_rfid_comp_set_callback(NULL, NULL); | ||||||
|  |     furi_hal_rfid_pins_reset(); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ibutton_worker_read_dallas(iButtonWorker* worker) { | ||||||
|  |     bool result = false; | ||||||
|  |     onewire_host_start(worker->host); | ||||||
|  |     delay(100); | ||||||
|  |     FURI_CRITICAL_ENTER(); | ||||||
|  |     if(onewire_host_search(worker->host, worker->key_data, NORMAL_SEARCH)) { | ||||||
|  |         onewire_host_reset_search(worker->host); | ||||||
|  | 
 | ||||||
|  |         // key found, verify
 | ||||||
|  |         if(onewire_host_reset(worker->host)) { | ||||||
|  |             onewire_host_write(worker->host, DS1990_CMD_READ_ROM); | ||||||
|  |             bool key_valid = true; | ||||||
|  |             for(uint8_t i = 0; i < ibutton_key_get_max_size(); i++) { | ||||||
|  |                 if(onewire_host_read(worker->host) != worker->key_data[i]) { | ||||||
|  |                     key_valid = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(key_valid) { | ||||||
|  |                 result = true; | ||||||
|  | 
 | ||||||
|  |                 furi_check(worker->key_p != NULL); | ||||||
|  |                 ibutton_key_set_type(worker->key_p, iButtonKeyDS1990); | ||||||
|  |                 ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         onewire_host_reset_search(worker->host); | ||||||
|  |     } | ||||||
|  |     onewire_host_stop(worker->host); | ||||||
|  |     FURI_CRITICAL_EXIT(); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_read_start(iButtonWorker* worker) { | ||||||
|  |     furi_hal_power_enable_otg(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_read_tick(iButtonWorker* worker) { | ||||||
|  |     bool valid = false; | ||||||
|  |     if(ibutton_worker_read_dallas(worker)) { | ||||||
|  |         valid = true; | ||||||
|  |     } else if(ibutton_worker_read_comparator(worker)) { | ||||||
|  |         valid = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(valid) { | ||||||
|  |         if(worker->read_cb != NULL) { | ||||||
|  |             worker->read_cb(worker->cb_ctx); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ibutton_worker_switch_mode(worker, iButtonWorkerIdle); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_read_stop(iButtonWorker* worker) { | ||||||
|  |     furi_hal_power_disable_otg(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*********************** EMULATE ***********************/ | ||||||
|  | static void onewire_slave_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     iButtonWorker* worker = context; | ||||||
|  |     if(worker->emulate_cb != NULL) { | ||||||
|  |         worker->emulate_cb(worker->cb_ctx, true); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) { | ||||||
|  |     uint8_t* device_id = onewire_device_get_id_p(worker->device); | ||||||
|  |     const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); | ||||||
|  |     const uint8_t key_size = ibutton_key_get_max_size(); | ||||||
|  |     memcpy(device_id, key_id, key_size); | ||||||
|  | 
 | ||||||
|  |     onewire_slave_attach(worker->slave, worker->device); | ||||||
|  |     onewire_slave_start(worker->slave); | ||||||
|  |     onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) { | ||||||
|  |     onewire_slave_stop(worker->slave); | ||||||
|  |     onewire_slave_detach(worker->slave); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_timer_cb(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     iButtonWorker* worker = context; | ||||||
|  | 
 | ||||||
|  |     bool polarity; | ||||||
|  |     uint32_t length; | ||||||
|  | 
 | ||||||
|  |     switch(worker->emulate_mode) { | ||||||
|  |     case iButtonEmulateModeCyfral: | ||||||
|  |         encoder_cyfral_get_pulse(worker->encoder_cyfral, &polarity, &length); | ||||||
|  |         break; | ||||||
|  |     case iButtonEmulateModeMetakom: | ||||||
|  |         encoder_metakom_get_pulse(worker->encoder_metakom, &polarity, &length); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_ibutton_emulate_set_next(length); | ||||||
|  | 
 | ||||||
|  |     if(polarity) { | ||||||
|  |         furi_hal_ibutton_pin_high(); | ||||||
|  |     } else { | ||||||
|  |         furi_hal_ibutton_pin_low(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { | ||||||
|  |     furi_assert(worker->key_p); | ||||||
|  |     const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p); | ||||||
|  |     const uint8_t key_size = ibutton_key_get_max_size(); | ||||||
|  | 
 | ||||||
|  |     switch(ibutton_key_get_type(worker->key_p)) { | ||||||
|  |     case iButtonKeyDS1990: | ||||||
|  |         return; | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyCyfral: | ||||||
|  |         worker->emulate_mode = iButtonEmulateModeCyfral; | ||||||
|  |         encoder_cyfral_reset(worker->encoder_cyfral); | ||||||
|  |         encoder_cyfral_set_data(worker->encoder_cyfral, key_id, key_size); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyMetakom: | ||||||
|  |         worker->emulate_mode = iButtonEmulateModeMetakom; | ||||||
|  |         encoder_metakom_reset(worker->encoder_metakom); | ||||||
|  |         encoder_metakom_set_data(worker->encoder_metakom, key_id, key_size); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_ibutton_start_drive(); | ||||||
|  |     furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_emulate_timer_stop(iButtonWorker* worker) { | ||||||
|  |     furi_hal_ibutton_emulate_stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_emulate_start(iButtonWorker* worker) { | ||||||
|  |     furi_assert(worker->key_p); | ||||||
|  | 
 | ||||||
|  |     furi_hal_rfid_pins_reset(); | ||||||
|  |     furi_hal_rfid_pin_pull_pulldown(); | ||||||
|  | 
 | ||||||
|  |     switch(ibutton_key_get_type(worker->key_p)) { | ||||||
|  |     case iButtonKeyDS1990: | ||||||
|  |         ibutton_worker_emulate_dallas_start(worker); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyCyfral: | ||||||
|  |     case iButtonKeyMetakom: | ||||||
|  |         ibutton_worker_emulate_timer_start(worker); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { | ||||||
|  |     furi_assert(worker->key_p); | ||||||
|  | 
 | ||||||
|  |     furi_hal_rfid_pins_reset(); | ||||||
|  | 
 | ||||||
|  |     switch(ibutton_key_get_type(worker->key_p)) { | ||||||
|  |     case iButtonKeyDS1990: | ||||||
|  |         ibutton_worker_emulate_dallas_stop(worker); | ||||||
|  |         break; | ||||||
|  |     case iButtonKeyCyfral: | ||||||
|  |     case iButtonKeyMetakom: | ||||||
|  |         ibutton_worker_emulate_timer_stop(worker); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*********************** WRITE ***********************/ | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_write_start(iButtonWorker* worker) { | ||||||
|  |     furi_hal_power_enable_otg(); | ||||||
|  |     onewire_host_start(worker->host); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_write_tick(iButtonWorker* worker) { | ||||||
|  |     furi_check(worker->key_p != NULL); | ||||||
|  |     iButtonWriterResult writer_result = ibutton_writer_write(worker->writer, worker->key_p); | ||||||
|  |     iButtonWorkerWriteResult result; | ||||||
|  |     switch(writer_result) { | ||||||
|  |     case iButtonWriterOK: | ||||||
|  |         result = iButtonWorkerWriteOK; | ||||||
|  |         break; | ||||||
|  |     case iButtonWriterSameKey: | ||||||
|  |         result = iButtonWorkerWriteSameKey; | ||||||
|  |         break; | ||||||
|  |     case iButtonWriterNoDetect: | ||||||
|  |         result = iButtonWorkerWriteNoDetect; | ||||||
|  |         break; | ||||||
|  |     case iButtonWriterCannotWrite: | ||||||
|  |         result = iButtonWorkerWriteCannotWrite; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         result = iButtonWorkerWriteNoDetect; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(worker->write_cb != NULL) { | ||||||
|  |         worker->write_cb(worker->cb_ctx, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_worker_mode_write_stop(iButtonWorker* worker) { | ||||||
|  |     furi_hal_power_disable_otg(); | ||||||
|  |     onewire_host_stop(worker->host); | ||||||
|  | } | ||||||
							
								
								
									
										298
									
								
								lib/one_wire/ibutton/ibutton_writer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								lib/one_wire/ibutton/ibutton_writer.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,298 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include "ibutton_writer.h" | ||||||
|  | #include "ibutton_key_command.h" | ||||||
|  | 
 | ||||||
|  | /*********************** PRIVATE ***********************/ | ||||||
|  | 
 | ||||||
|  | struct iButtonWriter { | ||||||
|  |     OneWireHost* host; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void writer_write_one_bit(iButtonWriter* writer, bool value, uint32_t delay) { | ||||||
|  |     onewire_host_write_bit(writer->host, value); | ||||||
|  |     delay_us(delay); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void writer_write_byte_ds1990(iButtonWriter* writer, uint8_t data) { | ||||||
|  |     for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { | ||||||
|  |         onewire_host_write_bit(writer->host, data & 1); | ||||||
|  |         delay_us(5000); | ||||||
|  |         data = data >> 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool writer_compare_key_ds1990(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  |         bool presence = onewire_host_reset(writer->host); | ||||||
|  | 
 | ||||||
|  |         if(presence) { | ||||||
|  |             onewire_host_write(writer->host, DS1990_CMD_READ_ROM); | ||||||
|  | 
 | ||||||
|  |             result = true; | ||||||
|  |             for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { | ||||||
|  |                 if(ibutton_key_get_data_p(key)[i] != onewire_host_read(writer->host)) { | ||||||
|  |                     result = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     uint8_t answer; | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|  |         // write rom, addr is 0x0000
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, TM2004_CMD_WRITE_ROM); | ||||||
|  |         onewire_host_write(writer->host, 0x00); | ||||||
|  |         onewire_host_write(writer->host, 0x00); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { | ||||||
|  |             // write key byte
 | ||||||
|  |             onewire_host_write(writer->host, ibutton_key_get_data_p(key)[i]); | ||||||
|  |             answer = onewire_host_read(writer->host); | ||||||
|  |             // TODO: check answer CRC
 | ||||||
|  | 
 | ||||||
|  |             // pulse indicating that data is correct
 | ||||||
|  |             delay_us(600); | ||||||
|  |             writer_write_one_bit(writer, 1, 50000); | ||||||
|  | 
 | ||||||
|  |             // read writed key byte
 | ||||||
|  |             answer = onewire_host_read(writer->host); | ||||||
|  | 
 | ||||||
|  |             // check that writed and readed are same
 | ||||||
|  |             if(ibutton_key_get_data_p(key)[i] != answer) { | ||||||
|  |                 result = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!writer_compare_key_ds1990(writer, key)) { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  |     } else { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool writer_write_1990_1(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|  |         // unlock
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); | ||||||
|  |         delay_us(10); | ||||||
|  |         writer_write_one_bit(writer, 0, 5000); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, RW1990_1_CMD_WRITE_ROM); | ||||||
|  |         for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { | ||||||
|  |             // inverted key for RW1990.1
 | ||||||
|  |             writer_write_byte_ds1990(writer, ~ibutton_key_get_data_p(key)[i]); | ||||||
|  |             delay_us(30000); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // lock
 | ||||||
|  |         onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG); | ||||||
|  |         writer_write_one_bit(writer, 1, 10000); | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  | 
 | ||||||
|  |         if(writer_compare_key_ds1990(writer, key)) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool writer_write_1990_2(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(ibutton_key_get_type(key) == iButtonKeyDS1990) { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|  |         // unlock
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); | ||||||
|  |         delay_us(10); | ||||||
|  |         writer_write_one_bit(writer, 1, 5000); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, RW1990_2_CMD_WRITE_ROM); | ||||||
|  |         for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) { | ||||||
|  |             writer_write_byte_ds1990(writer, ibutton_key_get_data_p(key)[i]); | ||||||
|  |             delay_us(30000); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // lock
 | ||||||
|  |         onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG); | ||||||
|  |         writer_write_one_bit(writer, 0, 10000); | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  | 
 | ||||||
|  |         if(writer_compare_key_ds1990(writer, key)) { | ||||||
|  |             result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | // TODO: adapt and test
 | ||||||
|  | static bool writer_write_TM01( | ||||||
|  |     iButtonWriter* writer, | ||||||
|  |     iButtonKey type, | ||||||
|  |     const uint8_t* key, | ||||||
|  |     uint8_t key_length) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         // TODO test and encoding
 | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|  |         // unlock
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         onewire_write_one_bit(1, 10000); | ||||||
|  | 
 | ||||||
|  |         // write key
 | ||||||
|  |         onewire_host_reset(writer->host); | ||||||
|  |         onewire_host_write(writer->host, TM01::CMD_WRITE_ROM); | ||||||
|  | 
 | ||||||
|  |         // TODO: key types
 | ||||||
|  |         //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
 | ||||||
|  |         //} else {
 | ||||||
|  |         for(uint8_t i = 0; i < key->get_type_data_size(); i++) { | ||||||
|  |             write_byte_ds1990(key->get_data()[i]); | ||||||
|  |             delay_us(10000); | ||||||
|  |         } | ||||||
|  |         //}
 | ||||||
|  | 
 | ||||||
|  |         // lock
 | ||||||
|  |         onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|  |         onewire_write_one_bit(0, 10000); | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!compare_key_ds1990(key)) { | ||||||
|  |         result = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|  |         if(key->get_key_type() == iButtonKeyType::KeyMetakom || | ||||||
|  |            key->get_key_type() == iButtonKeyType::KeyCyfral) { | ||||||
|  |             onewire_host_reset(writer->host); | ||||||
|  |             if(key->get_key_type() == iButtonKeyType::KeyCyfral) | ||||||
|  |                 onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_CYFRAL); | ||||||
|  |             else | ||||||
|  |                 onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_METAKOM); | ||||||
|  |             onewire_write_one_bit(1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | static iButtonWriterResult writer_write_DS1990(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     iButtonWriterResult result = iButtonWriterNoDetect; | ||||||
|  |     bool same_key = writer_compare_key_ds1990(writer, key); | ||||||
|  | 
 | ||||||
|  |     if(!same_key) { | ||||||
|  |         // currently we can write:
 | ||||||
|  |         // RW1990_1, TM08v2, TM08vi-2 by write_1990_1()
 | ||||||
|  |         // RW1990_2 by write_1990_2()
 | ||||||
|  |         // RW2004, RW2004, TM2004 with EEPROM by write_TM2004();
 | ||||||
|  | 
 | ||||||
|  |         bool write_result = true; | ||||||
|  |         do { | ||||||
|  |             if(writer_write_1990_1(writer, key)) break; | ||||||
|  |             if(writer_write_1990_2(writer, key)) break; | ||||||
|  |             if(writer_write_TM2004(writer, key)) break; | ||||||
|  |             write_result = false; | ||||||
|  |         } while(false); | ||||||
|  | 
 | ||||||
|  |         if(write_result) { | ||||||
|  |             result = iButtonWriterOK; | ||||||
|  |         } else { | ||||||
|  |             result = iButtonWriterCannotWrite; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         result = iButtonWriterSameKey; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*********************** PUBLIC ***********************/ | ||||||
|  | 
 | ||||||
|  | iButtonWriter* ibutton_writer_alloc(OneWireHost* host) { | ||||||
|  |     iButtonWriter* writer = malloc(sizeof(iButtonWriter)); | ||||||
|  |     writer->host = host; | ||||||
|  |     return writer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_writer_free(iButtonWriter* writer) { | ||||||
|  |     free(writer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key) { | ||||||
|  |     iButtonWriterResult result = iButtonWriterNoDetect; | ||||||
|  | 
 | ||||||
|  |     osKernelLock(); | ||||||
|  |     bool blank_present = onewire_host_reset(writer->host); | ||||||
|  |     osKernelUnlock(); | ||||||
|  | 
 | ||||||
|  |     if(blank_present) { | ||||||
|  |         switch(ibutton_key_get_type(key)) { | ||||||
|  |         case iButtonKeyDS1990: | ||||||
|  |             result = writer_write_DS1990(writer, key); | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_writer_start(iButtonWriter* writer) { | ||||||
|  |     furi_hal_power_enable_otg(); | ||||||
|  |     onewire_host_start(writer->host); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ibutton_writer_stop(iButtonWriter* writer) { | ||||||
|  |     onewire_host_stop(writer->host); | ||||||
|  |     furi_hal_power_disable_otg(); | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								lib/one_wire/ibutton/ibutton_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								lib/one_wire/ibutton/ibutton_writer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file ibutton_writer.h | ||||||
|  |  *  | ||||||
|  |  * iButton blanks writer | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <furi_hal_gpio.h> | ||||||
|  | #include "ibutton_key.h" | ||||||
|  | #include "../one_wire_host.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     iButtonWriterOK, | ||||||
|  |     iButtonWriterSameKey, | ||||||
|  |     iButtonWriterNoDetect, | ||||||
|  |     iButtonWriterCannotWrite, | ||||||
|  | } iButtonWriterResult; | ||||||
|  | 
 | ||||||
|  | typedef struct iButtonWriter iButtonWriter; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate writer | ||||||
|  |  * @param host  | ||||||
|  |  * @return iButtonWriter*  | ||||||
|  |  */ | ||||||
|  | iButtonWriter* ibutton_writer_alloc(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate writer | ||||||
|  |  * @param writer  | ||||||
|  |  */ | ||||||
|  | void ibutton_writer_free(iButtonWriter* writer); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write key to blank | ||||||
|  |  * @param writer  | ||||||
|  |  * @param key  | ||||||
|  |  * @return iButtonWriterResult  | ||||||
|  |  */ | ||||||
|  | iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start writing. Must be called before write attempt | ||||||
|  |  * @param writer  | ||||||
|  |  */ | ||||||
|  | void ibutton_writer_start(iButtonWriter* writer); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop writing | ||||||
|  |  * @param writer  | ||||||
|  |  */ | ||||||
|  | void ibutton_writer_stop(iButtonWriter* writer); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										256
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,256 @@ | |||||||
|  | #include "protocol_cyfral.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <furi/check.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
|  | 
 | ||||||
|  | #define CYFRAL_DATA_SIZE 2 | ||||||
|  | #define CYFRAL_MAX_PERIOD_US 230 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CYFRAL_BIT_WAIT_FRONT_HIGH, | ||||||
|  |     CYFRAL_BIT_WAIT_FRONT_LOW, | ||||||
|  | } CyfralBitState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CYFRAL_WAIT_START_NIBBLE, | ||||||
|  |     CYFRAL_READ_NIBBLE, | ||||||
|  |     CYFRAL_READ_STOP_NIBBLE, | ||||||
|  | } CyfralState; | ||||||
|  | 
 | ||||||
|  | struct ProtocolCyfral { | ||||||
|  |     PulseProtocol* protocol; | ||||||
|  | 
 | ||||||
|  |     CyfralState state; | ||||||
|  |     CyfralBitState bit_state; | ||||||
|  | 
 | ||||||
|  |     // ready flag, key is read and valid
 | ||||||
|  |     // TODO: atomic access
 | ||||||
|  |     bool ready; | ||||||
|  |     // key data storage
 | ||||||
|  |     uint16_t key_data; | ||||||
|  |     // high + low period time
 | ||||||
|  |     uint32_t period_time; | ||||||
|  |     // temporary nibble storage
 | ||||||
|  |     uint8_t nibble; | ||||||
|  |     // data valid flag
 | ||||||
|  |     // MUST be checked only in READ_STOP_NIBBLE state
 | ||||||
|  |     bool data_valid; | ||||||
|  |     // nibble index, we expect 8 nibbles
 | ||||||
|  |     uint8_t index; | ||||||
|  |     // bit index in nibble, 4 bit per nibble
 | ||||||
|  |     uint8_t bit_index; | ||||||
|  |     // max period, 230us x clock per us
 | ||||||
|  |     uint32_t max_period; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void cyfral_pulse(void* context, bool polarity, uint32_t length); | ||||||
|  | static void cyfral_reset(void* context); | ||||||
|  | static void cyfral_get_data(void* context, uint8_t* data, size_t length); | ||||||
|  | static bool cyfral_decoded(void* context); | ||||||
|  | 
 | ||||||
|  | ProtocolCyfral* protocol_cyfral_alloc() { | ||||||
|  |     ProtocolCyfral* cyfral = malloc(sizeof(ProtocolCyfral)); | ||||||
|  |     cyfral_reset(cyfral); | ||||||
|  | 
 | ||||||
|  |     cyfral->protocol = pulse_protocol_alloc(); | ||||||
|  | 
 | ||||||
|  |     pulse_protocol_set_context(cyfral->protocol, cyfral); | ||||||
|  |     pulse_protocol_set_pulse_cb(cyfral->protocol, cyfral_pulse); | ||||||
|  |     pulse_protocol_set_reset_cb(cyfral->protocol, cyfral_reset); | ||||||
|  |     pulse_protocol_set_get_data_cb(cyfral->protocol, cyfral_get_data); | ||||||
|  |     pulse_protocol_set_decoded_cb(cyfral->protocol, cyfral_decoded); | ||||||
|  | 
 | ||||||
|  |     return cyfral; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void protocol_cyfral_free(ProtocolCyfral* cyfral) { | ||||||
|  |     furi_assert(cyfral); | ||||||
|  |     pulse_protocol_free(cyfral->protocol); | ||||||
|  |     free(cyfral); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral) { | ||||||
|  |     furi_assert(cyfral); | ||||||
|  |     return cyfral->protocol; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cyfral_get_data(void* context, uint8_t* data, size_t length) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_check(length >= CYFRAL_DATA_SIZE); | ||||||
|  |     ProtocolCyfral* cyfral = context; | ||||||
|  |     memcpy(data, &cyfral->key_data, CYFRAL_DATA_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool cyfral_decoded(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolCyfral* cyfral = context; | ||||||
|  |     bool decoded = cyfral->ready; | ||||||
|  |     return decoded; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cyfral_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolCyfral* cyfral = context; | ||||||
|  |     cyfral->state = CYFRAL_WAIT_START_NIBBLE; | ||||||
|  |     cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; | ||||||
|  | 
 | ||||||
|  |     cyfral->period_time = 0; | ||||||
|  |     cyfral->bit_index = 0; | ||||||
|  |     cyfral->ready = false; | ||||||
|  |     cyfral->index = 0; | ||||||
|  | 
 | ||||||
|  |     cyfral->key_data = 0; | ||||||
|  |     cyfral->nibble = 0; | ||||||
|  |     cyfral->data_valid = true; | ||||||
|  | 
 | ||||||
|  |     cyfral->max_period = CYFRAL_MAX_PERIOD_US * instructions_per_us; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool cyfral_process_bit( | ||||||
|  |     ProtocolCyfral* cyfral, | ||||||
|  |     bool polarity, | ||||||
|  |     uint32_t length, | ||||||
|  |     bool* bit_ready, | ||||||
|  |     bool* bit_value) { | ||||||
|  |     bool result = true; | ||||||
|  |     *bit_ready = false; | ||||||
|  | 
 | ||||||
|  |     // bit start from low
 | ||||||
|  |     switch(cyfral->bit_state) { | ||||||
|  |     case CYFRAL_BIT_WAIT_FRONT_LOW: | ||||||
|  |         if(polarity == true) { | ||||||
|  |             cyfral->period_time += length; | ||||||
|  | 
 | ||||||
|  |             *bit_ready = true; | ||||||
|  |             if(cyfral->period_time <= cyfral->max_period) { | ||||||
|  |                 if((cyfral->period_time / 2) > length) { | ||||||
|  |                     *bit_value = false; | ||||||
|  |                 } else { | ||||||
|  |                     *bit_value = true; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 result = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CYFRAL_BIT_WAIT_FRONT_HIGH: | ||||||
|  |         if(polarity == false) { | ||||||
|  |             cyfral->period_time = length; | ||||||
|  |             cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cyfral_pulse(void* context, bool polarity, uint32_t length) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolCyfral* cyfral = context; | ||||||
|  | 
 | ||||||
|  |     bool bit_ready; | ||||||
|  |     bool bit_value; | ||||||
|  | 
 | ||||||
|  |     if(cyfral->ready) return; | ||||||
|  | 
 | ||||||
|  |     switch(cyfral->state) { | ||||||
|  |     case CYFRAL_WAIT_START_NIBBLE: | ||||||
|  |         // wait for start word
 | ||||||
|  |         if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { | ||||||
|  |             if(bit_ready) { | ||||||
|  |                 cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; | ||||||
|  |                 if(cyfral->nibble == 0b0001) { | ||||||
|  |                     cyfral->nibble = 0; | ||||||
|  |                     cyfral->state = CYFRAL_READ_NIBBLE; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             cyfral_reset(cyfral); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case CYFRAL_READ_NIBBLE: | ||||||
|  |         // read nibbles
 | ||||||
|  |         if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { | ||||||
|  |             if(bit_ready) { | ||||||
|  |                 cyfral->nibble = (cyfral->nibble << 1) | bit_value; | ||||||
|  | 
 | ||||||
|  |                 cyfral->bit_index++; | ||||||
|  | 
 | ||||||
|  |                 //convert every nibble to 2-bit index
 | ||||||
|  |                 if(cyfral->bit_index == 4) { | ||||||
|  |                     switch(cyfral->nibble) { | ||||||
|  |                     case 0b1110: | ||||||
|  |                         cyfral->key_data = (cyfral->key_data << 2) | 0b11; | ||||||
|  |                         break; | ||||||
|  |                     case 0b1101: | ||||||
|  |                         cyfral->key_data = (cyfral->key_data << 2) | 0b10; | ||||||
|  |                         break; | ||||||
|  |                     case 0b1011: | ||||||
|  |                         cyfral->key_data = (cyfral->key_data << 2) | 0b01; | ||||||
|  |                         break; | ||||||
|  |                     case 0b0111: | ||||||
|  |                         cyfral->key_data = (cyfral->key_data << 2) | 0b00; | ||||||
|  |                         break; | ||||||
|  |                     default: | ||||||
|  |                         cyfral->data_valid = false; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     cyfral->nibble = 0; | ||||||
|  |                     cyfral->bit_index = 0; | ||||||
|  |                     cyfral->index++; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // succefully read 8 nibbles
 | ||||||
|  |                 if(cyfral->index == 8) { | ||||||
|  |                     cyfral->state = CYFRAL_READ_STOP_NIBBLE; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             cyfral_reset(cyfral); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case CYFRAL_READ_STOP_NIBBLE: | ||||||
|  |         // read stop nibble
 | ||||||
|  |         if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { | ||||||
|  |             if(bit_ready) { | ||||||
|  |                 cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; | ||||||
|  |                 cyfral->bit_index++; | ||||||
|  | 
 | ||||||
|  |                 switch(cyfral->bit_index) { | ||||||
|  |                 case 0: | ||||||
|  |                 case 1: | ||||||
|  |                 case 2: | ||||||
|  |                 case 3: | ||||||
|  |                     break; | ||||||
|  |                 case 4: | ||||||
|  |                     if(cyfral->nibble == 0b0001) { | ||||||
|  |                         // validate data
 | ||||||
|  |                         if(cyfral->data_valid) { | ||||||
|  |                             cyfral->ready = true; | ||||||
|  |                         } else { | ||||||
|  |                             cyfral_reset(cyfral); | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         cyfral_reset(cyfral); | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     cyfral_reset(cyfral); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             cyfral_reset(cyfral); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file protocol_cyfral.h | ||||||
|  |  *  | ||||||
|  |  * Cyfral pulse format decoder | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "../../pulse_protocols/pulse_protocol.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct ProtocolCyfral ProtocolCyfral; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate decoder | ||||||
|  |  * @return ProtocolCyfral*  | ||||||
|  |  */ | ||||||
|  | ProtocolCyfral* protocol_cyfral_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate decoder | ||||||
|  |  * @param cyfral  | ||||||
|  |  */ | ||||||
|  | void protocol_cyfral_free(ProtocolCyfral* cyfral); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get protocol interface | ||||||
|  |  * @param cyfral  | ||||||
|  |  * @return PulseProtocol*  | ||||||
|  |  */ | ||||||
|  | PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										262
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,262 @@ | |||||||
|  | #include "protocol_metakom.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <furi/check.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
|  | 
 | ||||||
|  | #define METAKOM_DATA_SIZE 4 | ||||||
|  | #define METAKOM_PERIOD_SAMPLE_COUNT 10 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     METAKOM_WAIT_PERIOD_SYNC, | ||||||
|  |     METAKOM_WAIT_START_BIT, | ||||||
|  |     METAKOM_WAIT_START_WORD, | ||||||
|  |     METAKOM_READ_WORD, | ||||||
|  |     METAKOM_READ_STOP_WORD, | ||||||
|  | } MetakomState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     METAKOM_BIT_WAIT_FRONT_HIGH, | ||||||
|  |     METAKOM_BIT_WAIT_FRONT_LOW, | ||||||
|  | } MetakomBitState; | ||||||
|  | 
 | ||||||
|  | struct ProtocolMetakom { | ||||||
|  |     PulseProtocol* protocol; | ||||||
|  | 
 | ||||||
|  |     // high + low period time
 | ||||||
|  |     uint32_t period_time; | ||||||
|  |     uint32_t low_time_storage; | ||||||
|  |     uint8_t period_sample_index; | ||||||
|  |     uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT]; | ||||||
|  | 
 | ||||||
|  |     // ready flag
 | ||||||
|  |     // TODO: atomic access
 | ||||||
|  |     bool ready; | ||||||
|  | 
 | ||||||
|  |     uint8_t tmp_data; | ||||||
|  |     uint8_t tmp_counter; | ||||||
|  | 
 | ||||||
|  |     uint32_t key_data; | ||||||
|  |     uint8_t key_data_index; | ||||||
|  | 
 | ||||||
|  |     MetakomBitState bit_state; | ||||||
|  |     MetakomState state; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void metakom_pulse(void* context, bool polarity, uint32_t length); | ||||||
|  | static void metakom_reset(void* context); | ||||||
|  | static void metakom_get_data(void* context, uint8_t* data, size_t length); | ||||||
|  | static bool metakom_decoded(void* context); | ||||||
|  | 
 | ||||||
|  | ProtocolMetakom* protocol_metakom_alloc() { | ||||||
|  |     ProtocolMetakom* metakom = malloc(sizeof(ProtocolMetakom)); | ||||||
|  |     metakom_reset(metakom); | ||||||
|  | 
 | ||||||
|  |     metakom->protocol = pulse_protocol_alloc(); | ||||||
|  | 
 | ||||||
|  |     pulse_protocol_set_context(metakom->protocol, metakom); | ||||||
|  |     pulse_protocol_set_pulse_cb(metakom->protocol, metakom_pulse); | ||||||
|  |     pulse_protocol_set_reset_cb(metakom->protocol, metakom_reset); | ||||||
|  |     pulse_protocol_set_get_data_cb(metakom->protocol, metakom_get_data); | ||||||
|  |     pulse_protocol_set_decoded_cb(metakom->protocol, metakom_decoded); | ||||||
|  | 
 | ||||||
|  |     return metakom; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void protocol_metakom_free(ProtocolMetakom* metakom) { | ||||||
|  |     furi_assert(metakom); | ||||||
|  |     pulse_protocol_free(metakom->protocol); | ||||||
|  |     free(metakom); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom) { | ||||||
|  |     furi_assert(metakom); | ||||||
|  |     return metakom->protocol; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void metakom_get_data(void* context, uint8_t* data, size_t length) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_check(length >= METAKOM_DATA_SIZE); | ||||||
|  |     ProtocolMetakom* metakom = context; | ||||||
|  |     memcpy(data, &metakom->key_data, METAKOM_DATA_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool metakom_decoded(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolMetakom* metakom = context; | ||||||
|  |     bool decoded = metakom->ready; | ||||||
|  |     return decoded; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void metakom_reset(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolMetakom* metakom = context; | ||||||
|  | 
 | ||||||
|  |     metakom->ready = false; | ||||||
|  |     metakom->period_sample_index = 0; | ||||||
|  |     metakom->period_time = 0; | ||||||
|  |     metakom->tmp_counter = 0; | ||||||
|  |     metakom->tmp_data = 0; | ||||||
|  |     for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { | ||||||
|  |         metakom->period_sample_data[i] = 0; | ||||||
|  |     }; | ||||||
|  |     metakom->state = METAKOM_WAIT_PERIOD_SYNC; | ||||||
|  |     metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; | ||||||
|  |     metakom->key_data = 0; | ||||||
|  |     metakom->key_data_index = 0; | ||||||
|  |     metakom->low_time_storage = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool metakom_parity_check(uint8_t data) { | ||||||
|  |     uint8_t ones_count = 0; | ||||||
|  |     bool result; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < 8; i++) { | ||||||
|  |         if((data >> i) & 0b00000001) { | ||||||
|  |             ones_count++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = (ones_count % 2 == 0); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool metakom_process_bit( | ||||||
|  |     ProtocolMetakom* metakom, | ||||||
|  |     bool polarity, | ||||||
|  |     uint32_t time, | ||||||
|  |     uint32_t* high_time, | ||||||
|  |     uint32_t* low_time) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     switch(metakom->bit_state) { | ||||||
|  |     case METAKOM_BIT_WAIT_FRONT_LOW: | ||||||
|  |         if(polarity == false) { | ||||||
|  |             *low_time = metakom->low_time_storage; | ||||||
|  |             *high_time = time; | ||||||
|  |             result = true; | ||||||
|  |             metakom->bit_state = METAKOM_BIT_WAIT_FRONT_HIGH; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case METAKOM_BIT_WAIT_FRONT_HIGH: | ||||||
|  |         if(polarity == true) { | ||||||
|  |             metakom->low_time_storage = time; | ||||||
|  |             metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void metakom_pulse(void* context, bool polarity, uint32_t time) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     ProtocolMetakom* metakom = context; | ||||||
|  | 
 | ||||||
|  |     if(metakom->ready) return; | ||||||
|  | 
 | ||||||
|  |     uint32_t high_time = 0; | ||||||
|  |     uint32_t low_time = 0; | ||||||
|  | 
 | ||||||
|  |     switch(metakom->state) { | ||||||
|  |     case METAKOM_WAIT_PERIOD_SYNC: | ||||||
|  |         if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { | ||||||
|  |             metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time; | ||||||
|  |             metakom->period_sample_index++; | ||||||
|  | 
 | ||||||
|  |             if(metakom->period_sample_index == METAKOM_PERIOD_SAMPLE_COUNT) { | ||||||
|  |                 for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { | ||||||
|  |                     metakom->period_time += metakom->period_sample_data[i]; | ||||||
|  |                 }; | ||||||
|  |                 metakom->period_time /= METAKOM_PERIOD_SAMPLE_COUNT; | ||||||
|  | 
 | ||||||
|  |                 metakom->state = METAKOM_WAIT_START_BIT; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case METAKOM_WAIT_START_BIT: | ||||||
|  |         if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { | ||||||
|  |             metakom->tmp_counter++; | ||||||
|  |             if(high_time > metakom->period_time) { | ||||||
|  |                 metakom->tmp_counter = 0; | ||||||
|  |                 metakom->state = METAKOM_WAIT_START_WORD; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(metakom->tmp_counter > 40) { | ||||||
|  |                 metakom_reset(metakom); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     case METAKOM_WAIT_START_WORD: | ||||||
|  |         if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (metakom->period_time / 2)) { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             metakom->tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(metakom->tmp_counter == 3) { | ||||||
|  |                 if(metakom->tmp_data == 0b010) { | ||||||
|  |                     metakom->tmp_counter = 0; | ||||||
|  |                     metakom->tmp_data = 0; | ||||||
|  |                     metakom->state = METAKOM_READ_WORD; | ||||||
|  |                 } else { | ||||||
|  |                     metakom_reset(metakom); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case METAKOM_READ_WORD: | ||||||
|  |         if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (metakom->period_time / 2)) { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             metakom->tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(metakom->tmp_counter == 8) { | ||||||
|  |                 if(metakom_parity_check(metakom->tmp_data)) { | ||||||
|  |                     metakom->key_data = (metakom->key_data << 8) | metakom->tmp_data; | ||||||
|  |                     metakom->key_data_index++; | ||||||
|  |                     metakom->tmp_data = 0; | ||||||
|  |                     metakom->tmp_counter = 0; | ||||||
|  | 
 | ||||||
|  |                     if(metakom->key_data_index == 4) { | ||||||
|  |                         // check for stop bit
 | ||||||
|  |                         if(high_time > metakom->period_time) { | ||||||
|  |                             metakom->state = METAKOM_READ_STOP_WORD; | ||||||
|  |                         } else { | ||||||
|  |                             metakom_reset(metakom); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     metakom_reset(metakom); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case METAKOM_READ_STOP_WORD: | ||||||
|  |         if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { | ||||||
|  |             if(low_time < (metakom->period_time / 2)) { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; | ||||||
|  |             } else { | ||||||
|  |                 metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; | ||||||
|  |             } | ||||||
|  |             metakom->tmp_counter++; | ||||||
|  | 
 | ||||||
|  |             if(metakom->tmp_counter == 3) { | ||||||
|  |                 if(metakom->tmp_data == 0b010) { | ||||||
|  |                     metakom->ready = true; | ||||||
|  |                 } else { | ||||||
|  |                     metakom_reset(metakom); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file protocol_metakom.h | ||||||
|  |  *  | ||||||
|  |  * Metakom pulse format decoder | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include "../../pulse_protocols/pulse_protocol.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct ProtocolMetakom ProtocolMetakom; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate decoder | ||||||
|  |  * @return ProtocolMetakom*  | ||||||
|  |  */ | ||||||
|  | ProtocolMetakom* protocol_metakom_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free decoder | ||||||
|  |  * @param metakom  | ||||||
|  |  */ | ||||||
|  | void protocol_metakom_free(ProtocolMetakom* metakom); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get protocol interface | ||||||
|  |  * @param metakom  | ||||||
|  |  * @return PulseProtocol*  | ||||||
|  |  */ | ||||||
|  | PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										16
									
								
								lib/one_wire/maxim_crc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/one_wire/maxim_crc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | #include "maxim_crc.h" | ||||||
|  | 
 | ||||||
|  | uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init) { | ||||||
|  |     uint8_t crc = crc_init; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t index = 0; index < data_size; ++index) { | ||||||
|  |         uint8_t input_byte = data[index]; | ||||||
|  |         for(uint8_t bit_position = 0; bit_position < 8; ++bit_position) { | ||||||
|  |             const uint8_t mix = (crc ^ input_byte) & (uint8_t)(0x01); | ||||||
|  |             crc >>= 1; | ||||||
|  |             if(mix != 0) crc ^= 0x8C; | ||||||
|  |             input_byte >>= 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return crc; | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								lib/one_wire/maxim_crc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/one_wire/maxim_crc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define MAXIM_CRC8_INIT 0 | ||||||
|  | 
 | ||||||
|  | uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										59
									
								
								lib/one_wire/one_wire_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								lib/one_wire/one_wire_device.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | #include <stdlib.h> | ||||||
|  | #include "maxim_crc.h" | ||||||
|  | #include "one_wire_device.h" | ||||||
|  | #include "one_wire_slave.h" | ||||||
|  | #include "one_wire_slave_i.h" | ||||||
|  | 
 | ||||||
|  | struct OneWireDevice { | ||||||
|  |     uint8_t id_storage[8]; | ||||||
|  |     OneWireSlave* bus; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | OneWireDevice* onewire_device_alloc( | ||||||
|  |     uint8_t id_1, | ||||||
|  |     uint8_t id_2, | ||||||
|  |     uint8_t id_3, | ||||||
|  |     uint8_t id_4, | ||||||
|  |     uint8_t id_5, | ||||||
|  |     uint8_t id_6, | ||||||
|  |     uint8_t id_7, | ||||||
|  |     uint8_t id_8) { | ||||||
|  |     OneWireDevice* device = malloc(sizeof(OneWireDevice)); | ||||||
|  |     device->id_storage[0] = id_1; | ||||||
|  |     device->id_storage[1] = id_2; | ||||||
|  |     device->id_storage[2] = id_3; | ||||||
|  |     device->id_storage[3] = id_4; | ||||||
|  |     device->id_storage[4] = id_5; | ||||||
|  |     device->id_storage[5] = id_6; | ||||||
|  |     device->id_storage[6] = id_7; | ||||||
|  |     device->id_storage[7] = id_8; | ||||||
|  |     device->bus = NULL; | ||||||
|  | 
 | ||||||
|  |     return device; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_device_free(OneWireDevice* device) { | ||||||
|  |     if(device->bus != NULL) { | ||||||
|  |         onewire_slave_detach(device->bus); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     free(device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_device_send_id(OneWireDevice* device) { | ||||||
|  |     if(device->bus != NULL) { | ||||||
|  |         onewire_slave_send(device->bus, device->id_storage, 8); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus) { | ||||||
|  |     device->bus = bus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_device_detach(OneWireDevice* device) { | ||||||
|  |     device->bus = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t* onewire_device_get_id_p(OneWireDevice* device) { | ||||||
|  |     return device->id_storage; | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								lib/one_wire/one_wire_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/one_wire/one_wire_device.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file one_wire_device.h | ||||||
|  |  *  | ||||||
|  |  * 1-Wire slave library, device interface. Currently it can only emulate ID. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct OneWireSlave OneWireSlave; | ||||||
|  | typedef struct OneWireDevice OneWireDevice; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate onewire device with ID | ||||||
|  |  * @param id_1  | ||||||
|  |  * @param id_2  | ||||||
|  |  * @param id_3  | ||||||
|  |  * @param id_4  | ||||||
|  |  * @param id_5  | ||||||
|  |  * @param id_6  | ||||||
|  |  * @param id_7  | ||||||
|  |  * @param id_8  | ||||||
|  |  * @return OneWireDevice*  | ||||||
|  |  */ | ||||||
|  | OneWireDevice* onewire_device_alloc( | ||||||
|  |     uint8_t id_1, | ||||||
|  |     uint8_t id_2, | ||||||
|  |     uint8_t id_3, | ||||||
|  |     uint8_t id_4, | ||||||
|  |     uint8_t id_5, | ||||||
|  |     uint8_t id_6, | ||||||
|  |     uint8_t id_7, | ||||||
|  |     uint8_t id_8); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate onewire device | ||||||
|  |  * @param device  | ||||||
|  |  */ | ||||||
|  | void onewire_device_free(OneWireDevice* device); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send ID report, called from onewire slave | ||||||
|  |  * @param device  | ||||||
|  |  */ | ||||||
|  | void onewire_device_send_id(OneWireDevice* device); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Attach device to onewire slave bus | ||||||
|  |  * @param device  | ||||||
|  |  * @param bus  | ||||||
|  |  */ | ||||||
|  | void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Attach device from onewire slave bus | ||||||
|  |  * @param device  | ||||||
|  |  */ | ||||||
|  | void onewire_device_detach(OneWireDevice* device); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get pointer to device id array | ||||||
|  |  * @param device  | ||||||
|  |  * @return uint8_t*  | ||||||
|  |  */ | ||||||
|  | uint8_t* onewire_device_get_id_p(OneWireDevice* device); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										261
									
								
								lib/one_wire/one_wire_host.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								lib/one_wire/one_wire_host.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include "one_wire_host.h" | ||||||
|  | #include "one_wire_host_timing.h" | ||||||
|  | 
 | ||||||
|  | struct OneWireHost { | ||||||
|  |     // global search state
 | ||||||
|  |     unsigned char saved_rom[8]; | ||||||
|  |     uint8_t last_discrepancy; | ||||||
|  |     uint8_t last_family_discrepancy; | ||||||
|  |     bool last_device_flag; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | OneWireHost* onewire_host_alloc() { | ||||||
|  |     OneWireHost* host = malloc(sizeof(OneWireHost)); | ||||||
|  |     onewire_host_reset_search(host); | ||||||
|  |     return host; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_free(OneWireHost* host) { | ||||||
|  |     onewire_host_stop(host); | ||||||
|  |     free(host); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_host_reset(OneWireHost* host) { | ||||||
|  |     uint8_t r; | ||||||
|  |     uint8_t retries = 125; | ||||||
|  | 
 | ||||||
|  |     // wait until the gpio is high
 | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  |     do { | ||||||
|  |         if(--retries == 0) return 0; | ||||||
|  |         delay_us(2); | ||||||
|  |     } while(!furi_hal_ibutton_pin_get_level()); | ||||||
|  | 
 | ||||||
|  |     // pre delay
 | ||||||
|  |     delay_us(OWH_RESET_DELAY_PRE); | ||||||
|  | 
 | ||||||
|  |     // drive low
 | ||||||
|  |     furi_hal_ibutton_pin_low(); | ||||||
|  |     delay_us(OWH_RESET_DRIVE); | ||||||
|  | 
 | ||||||
|  |     // release
 | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  |     delay_us(OWH_RESET_RELEASE); | ||||||
|  | 
 | ||||||
|  |     // read and post delay
 | ||||||
|  |     r = !furi_hal_ibutton_pin_get_level(); | ||||||
|  |     delay_us(OWH_RESET_DELAY_POST); | ||||||
|  | 
 | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_host_read_bit(OneWireHost* host) { | ||||||
|  |     bool result; | ||||||
|  | 
 | ||||||
|  |     // drive low
 | ||||||
|  |     furi_hal_ibutton_pin_low(); | ||||||
|  |     delay_us(OWH_READ_DRIVE); | ||||||
|  | 
 | ||||||
|  |     // release
 | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  |     delay_us(OWH_READ_RELEASE); | ||||||
|  | 
 | ||||||
|  |     // read and post delay
 | ||||||
|  |     result = furi_hal_ibutton_pin_get_level(); | ||||||
|  |     delay_us(OWH_READ_DELAY_POST); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t onewire_host_read(OneWireHost* host) { | ||||||
|  |     uint8_t result = 0; | ||||||
|  | 
 | ||||||
|  |     for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||||
|  |         if(onewire_host_read_bit(host)) { | ||||||
|  |             result |= bitMask; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) { | ||||||
|  |     for(uint16_t i = 0; i < count; i++) { | ||||||
|  |         buffer[i] = onewire_host_read(host); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_write_bit(OneWireHost* host, bool value) { | ||||||
|  |     if(value) { | ||||||
|  |         // drive low
 | ||||||
|  |         furi_hal_ibutton_pin_low(); | ||||||
|  |         delay_us(OWH_WRITE_1_DRIVE); | ||||||
|  | 
 | ||||||
|  |         // release
 | ||||||
|  |         furi_hal_ibutton_pin_high(); | ||||||
|  |         delay_us(OWH_WRITE_1_RELEASE); | ||||||
|  |     } else { | ||||||
|  |         // drive low
 | ||||||
|  |         furi_hal_ibutton_pin_low(); | ||||||
|  |         delay_us(OWH_WRITE_0_DRIVE); | ||||||
|  | 
 | ||||||
|  |         // release
 | ||||||
|  |         furi_hal_ibutton_pin_high(); | ||||||
|  |         delay_us(OWH_WRITE_0_RELEASE); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_write(OneWireHost* host, uint8_t value) { | ||||||
|  |     uint8_t bitMask; | ||||||
|  | 
 | ||||||
|  |     for(bitMask = 0x01; bitMask; bitMask <<= 1) { | ||||||
|  |         onewire_host_write_bit(host, (bitMask & value) ? 1 : 0); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_skip(OneWireHost* host) { | ||||||
|  |     onewire_host_write(host, 0xCC); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_start(OneWireHost* host) { | ||||||
|  |     furi_hal_ibutton_start_drive(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_stop(OneWireHost* host) { | ||||||
|  |     furi_hal_ibutton_stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_reset_search(OneWireHost* host) { | ||||||
|  |     host->last_discrepancy = 0; | ||||||
|  |     host->last_device_flag = false; | ||||||
|  |     host->last_family_discrepancy = 0; | ||||||
|  |     for(int i = 7;; i--) { | ||||||
|  |         host->saved_rom[i] = 0; | ||||||
|  |         if(i == 0) break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { | ||||||
|  |     host->saved_rom[0] = family_code; | ||||||
|  |     for(uint8_t i = 1; i < 8; i++) host->saved_rom[i] = 0; | ||||||
|  |     host->last_discrepancy = 64; | ||||||
|  |     host->last_family_discrepancy = 0; | ||||||
|  |     host->last_device_flag = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) { | ||||||
|  |     uint8_t id_bit_number; | ||||||
|  |     uint8_t last_zero, rom_byte_number, search_result; | ||||||
|  |     uint8_t id_bit, cmp_id_bit; | ||||||
|  | 
 | ||||||
|  |     unsigned char rom_byte_mask, search_direction; | ||||||
|  | 
 | ||||||
|  |     // initialize for search
 | ||||||
|  |     id_bit_number = 1; | ||||||
|  |     last_zero = 0; | ||||||
|  |     rom_byte_number = 0; | ||||||
|  |     rom_byte_mask = 1; | ||||||
|  |     search_result = 0; | ||||||
|  | 
 | ||||||
|  |     // if the last call was not the last one
 | ||||||
|  |     if(!host->last_device_flag) { | ||||||
|  |         // 1-Wire reset
 | ||||||
|  |         if(!onewire_host_reset(host)) { | ||||||
|  |             // reset the search
 | ||||||
|  |             host->last_discrepancy = 0; | ||||||
|  |             host->last_device_flag = false; | ||||||
|  |             host->last_family_discrepancy = 0; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // issue the search command
 | ||||||
|  |         switch(mode) { | ||||||
|  |         case CONDITIONAL_SEARCH: | ||||||
|  |             onewire_host_write(host, 0xEC); | ||||||
|  |             break; | ||||||
|  |         case NORMAL_SEARCH: | ||||||
|  |             onewire_host_write(host, 0xF0); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // loop to do the search
 | ||||||
|  |         do { | ||||||
|  |             // read a bit and its complement
 | ||||||
|  |             id_bit = onewire_host_read_bit(host); | ||||||
|  |             cmp_id_bit = onewire_host_read_bit(host); | ||||||
|  | 
 | ||||||
|  |             // check for no devices on 1-wire
 | ||||||
|  |             if((id_bit == 1) && (cmp_id_bit == 1)) | ||||||
|  |                 break; | ||||||
|  |             else { | ||||||
|  |                 // all devices coupled have 0 or 1
 | ||||||
|  |                 if(id_bit != cmp_id_bit) | ||||||
|  |                     search_direction = id_bit; // bit write value for search
 | ||||||
|  |                 else { | ||||||
|  |                     // if this discrepancy if before the Last Discrepancy
 | ||||||
|  |                     // on a previous next then pick the same as last time
 | ||||||
|  |                     if(id_bit_number < host->last_discrepancy) | ||||||
|  |                         search_direction = | ||||||
|  |                             ((host->saved_rom[rom_byte_number] & rom_byte_mask) > 0); | ||||||
|  |                     else | ||||||
|  |                         // if equal to last pick 1, if not then pick 0
 | ||||||
|  |                         search_direction = (id_bit_number == host->last_discrepancy); | ||||||
|  | 
 | ||||||
|  |                     // if 0 was picked then record its position in LastZero
 | ||||||
|  |                     if(search_direction == 0) { | ||||||
|  |                         last_zero = id_bit_number; | ||||||
|  | 
 | ||||||
|  |                         // check for Last discrepancy in family
 | ||||||
|  |                         if(last_zero < 9) host->last_family_discrepancy = last_zero; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // set or clear the bit in the ROM byte rom_byte_number
 | ||||||
|  |                 // with mask rom_byte_mask
 | ||||||
|  |                 if(search_direction == 1) | ||||||
|  |                     host->saved_rom[rom_byte_number] |= rom_byte_mask; | ||||||
|  |                 else | ||||||
|  |                     host->saved_rom[rom_byte_number] &= ~rom_byte_mask; | ||||||
|  | 
 | ||||||
|  |                 // serial number search direction write bit
 | ||||||
|  |                 onewire_host_write_bit(host, search_direction); | ||||||
|  | 
 | ||||||
|  |                 // increment the byte counter id_bit_number
 | ||||||
|  |                 // and shift the mask rom_byte_mask
 | ||||||
|  |                 id_bit_number++; | ||||||
|  |                 rom_byte_mask <<= 1; | ||||||
|  | 
 | ||||||
|  |                 // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
 | ||||||
|  |                 if(rom_byte_mask == 0) { | ||||||
|  |                     rom_byte_number++; | ||||||
|  |                     rom_byte_mask = 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
 | ||||||
|  | 
 | ||||||
|  |         // if the search was successful then
 | ||||||
|  |         if(!(id_bit_number < 65)) { | ||||||
|  |             // search successful so set last_Discrepancy, last_device_flag, search_result
 | ||||||
|  |             host->last_discrepancy = last_zero; | ||||||
|  | 
 | ||||||
|  |             // check for last device
 | ||||||
|  |             if(host->last_discrepancy == 0) host->last_device_flag = true; | ||||||
|  | 
 | ||||||
|  |             search_result = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // if no device found then reset counters so next 'search' will be like a first
 | ||||||
|  |     if(!search_result || !host->saved_rom[0]) { | ||||||
|  |         host->last_discrepancy = 0; | ||||||
|  |         host->last_device_flag = false; | ||||||
|  |         host->last_family_discrepancy = 0; | ||||||
|  |         search_result = false; | ||||||
|  |     } else { | ||||||
|  |         for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return search_result; | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								lib/one_wire/one_wire_host.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								lib/one_wire/one_wire_host.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file one_wire_host.h | ||||||
|  |  *  | ||||||
|  |  * 1-Wire host (master) library | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <furi_hal_gpio.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CONDITIONAL_SEARCH = 0, /**< Search for alarmed device */ | ||||||
|  |     NORMAL_SEARCH = 1, /**< Search all devices */ | ||||||
|  | } OneWireHostSearchMode; | ||||||
|  | 
 | ||||||
|  | typedef struct OneWireHost OneWireHost; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate onewire host bus | ||||||
|  |  * @param gpio  | ||||||
|  |  * @return OneWireHost*  | ||||||
|  |  */ | ||||||
|  | OneWireHost* onewire_host_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate onewire host bus | ||||||
|  |  * @param host  | ||||||
|  |  */ | ||||||
|  | void onewire_host_free(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset bus | ||||||
|  |  * @param host  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool onewire_host_reset(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read one bit | ||||||
|  |  * @param host  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool onewire_host_read_bit(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read one byte | ||||||
|  |  * @param host  | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t onewire_host_read(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Read many bytes | ||||||
|  |  * @param host  | ||||||
|  |  * @param buffer  | ||||||
|  |  * @param count  | ||||||
|  |  */ | ||||||
|  | void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write one bit | ||||||
|  |  * @param host  | ||||||
|  |  * @param value  | ||||||
|  |  */ | ||||||
|  | void onewire_host_write_bit(OneWireHost* host, bool value); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Write one byte | ||||||
|  |  * @param host  | ||||||
|  |  * @param value  | ||||||
|  |  */ | ||||||
|  | void onewire_host_write(OneWireHost* host, uint8_t value); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Skip ROM command | ||||||
|  |  * @param host  | ||||||
|  |  */ | ||||||
|  | void onewire_host_skip(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start working with the bus | ||||||
|  |  * @param host  | ||||||
|  |  */ | ||||||
|  | void onewire_host_start(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop working with the bus | ||||||
|  |  * @param host  | ||||||
|  |  */ | ||||||
|  | void onewire_host_stop(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  *  | ||||||
|  |  * @param host  | ||||||
|  |  */ | ||||||
|  | void onewire_host_reset_search(OneWireHost* host); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  *  | ||||||
|  |  * @param host  | ||||||
|  |  * @param family_code  | ||||||
|  |  */ | ||||||
|  | void onewire_host_target_search(OneWireHost* host, uint8_t family_code); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  *  | ||||||
|  |  * @param host  | ||||||
|  |  * @param newAddr  | ||||||
|  |  * @param mode  | ||||||
|  |  * @return uint8_t  | ||||||
|  |  */ | ||||||
|  | uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										30
									
								
								lib/one_wire/one_wire_host_timing.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/one_wire/one_wire_host_timing.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file one_wire_host_timing.h | ||||||
|  |  *  | ||||||
|  |  * 1-Wire library, timing list | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #define OWH_TIMING_A 9 | ||||||
|  | #define OWH_TIMING_B 64 | ||||||
|  | #define OWH_TIMING_C 64 | ||||||
|  | #define OWH_TIMING_D 14 | ||||||
|  | #define OWH_TIMING_E 9 | ||||||
|  | #define OWH_TIMING_F 55 | ||||||
|  | #define OWH_TIMING_G 0 | ||||||
|  | #define OWH_TIMING_H 480 | ||||||
|  | #define OWH_TIMING_I 70 | ||||||
|  | #define OWH_TIMING_J 410 | ||||||
|  | 
 | ||||||
|  | #define OWH_WRITE_1_DRIVE OWH_TIMING_A | ||||||
|  | #define OWH_WRITE_1_RELEASE OWH_TIMING_B | ||||||
|  | #define OWH_WRITE_0_DRIVE OWH_TIMING_C | ||||||
|  | #define OWH_WRITE_0_RELEASE OWH_TIMING_D | ||||||
|  | #define OWH_READ_DRIVE 3 | ||||||
|  | #define OWH_READ_RELEASE OWH_TIMING_E | ||||||
|  | #define OWH_READ_DELAY_POST OWH_TIMING_F | ||||||
|  | #define OWH_RESET_DELAY_PRE OWH_TIMING_G | ||||||
|  | #define OWH_RESET_DRIVE OWH_TIMING_H | ||||||
|  | #define OWH_RESET_RELEASE OWH_TIMING_I | ||||||
|  | #define OWH_RESET_DELAY_POST OWH_TIMING_J | ||||||
							
								
								
									
										317
									
								
								lib/one_wire/one_wire_slave.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								lib/one_wire/one_wire_slave.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,317 @@ | |||||||
|  | #include "one_wire_slave.h" | ||||||
|  | #include "one_wire_slave_i.h" | ||||||
|  | #include "one_wire_device.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
|  | #include <furi_hal_ibutton.h> | ||||||
|  | 
 | ||||||
|  | #define OWS_RESET_MIN 270 | ||||||
|  | #define OWS_RESET_MAX 960 | ||||||
|  | #define OWS_PRESENCE_TIMEOUT 20 | ||||||
|  | #define OWS_PRESENCE_MIN 100 | ||||||
|  | #define OWS_PRESENCE_MAX 480 | ||||||
|  | #define OWS_MSG_HIGH_TIMEOUT 15000 | ||||||
|  | #define OWS_SLOT_MAX 135 | ||||||
|  | #define OWS_READ_MIN 20 | ||||||
|  | #define OWS_READ_MAX 60 | ||||||
|  | #define OWS_WRITE_ZERO 30 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     NO_ERROR = 0, | ||||||
|  |     VERY_LONG_RESET, | ||||||
|  |     VERY_SHORT_RESET, | ||||||
|  |     PRESENCE_LOW_ON_LINE, | ||||||
|  |     AWAIT_TIMESLOT_TIMEOUT_HIGH, | ||||||
|  |     INCORRECT_ONEWIRE_CMD, | ||||||
|  |     FIRST_BIT_OF_BYTE_TIMEOUT, | ||||||
|  |     RESET_IN_PROGRESS | ||||||
|  | } OneWireSlaveError; | ||||||
|  | 
 | ||||||
|  | struct OneWireSlave { | ||||||
|  |     OneWireSlaveError error; | ||||||
|  |     OneWireDevice* device; | ||||||
|  |     OneWireSlaveResultCallback result_cb; | ||||||
|  |     void* result_cb_ctx; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*********************** PRIVATE ***********************/ | ||||||
|  | 
 | ||||||
|  | uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { | ||||||
|  |     uint32_t start = DWT->CYCCNT; | ||||||
|  |     uint32_t time_ticks = time * instructions_per_us; | ||||||
|  |     uint32_t time_captured; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         time_captured = DWT->CYCCNT; | ||||||
|  |         if(furi_hal_ibutton_pin_get_level() != pin_value) { | ||||||
|  |             uint32_t remaining_time = time_ticks - (time_captured - start); | ||||||
|  |             remaining_time /= instructions_per_us; | ||||||
|  |             return remaining_time; | ||||||
|  |         } | ||||||
|  |     } while((time_captured - start) < time_ticks); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_show_presence(OneWireSlave* bus) { | ||||||
|  |     // wait while master delay presence check
 | ||||||
|  |     onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); | ||||||
|  | 
 | ||||||
|  |     // show presence
 | ||||||
|  |     furi_hal_ibutton_pin_low(); | ||||||
|  |     delay_us(OWS_PRESENCE_MIN); | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  | 
 | ||||||
|  |     // somebody also can show presence
 | ||||||
|  |     const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; | ||||||
|  | 
 | ||||||
|  |     // so we will wait
 | ||||||
|  |     if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) { | ||||||
|  |         bus->error = PRESENCE_LOW_ON_LINE; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_receive_bit(OneWireSlave* bus) { | ||||||
|  |     // wait while bus is low
 | ||||||
|  |     uint32_t time = OWS_SLOT_MAX; | ||||||
|  |     time = onewire_slave_wait_while_gpio_is(bus, time, false); | ||||||
|  |     if(time == 0) { | ||||||
|  |         bus->error = RESET_IN_PROGRESS; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // wait while bus is high
 | ||||||
|  |     time = OWS_MSG_HIGH_TIMEOUT; | ||||||
|  |     time = onewire_slave_wait_while_gpio_is(bus, time, true); | ||||||
|  |     if(time == 0) { | ||||||
|  |         bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // wait a time of zero
 | ||||||
|  |     time = OWS_READ_MIN; | ||||||
|  |     time = onewire_slave_wait_while_gpio_is(bus, time, false); | ||||||
|  | 
 | ||||||
|  |     return (time > 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { | ||||||
|  |     const bool write_zero = !value; | ||||||
|  | 
 | ||||||
|  |     // wait while bus is low
 | ||||||
|  |     uint32_t time = OWS_SLOT_MAX; | ||||||
|  |     time = onewire_slave_wait_while_gpio_is(bus, time, false); | ||||||
|  |     if(time == 0) { | ||||||
|  |         bus->error = RESET_IN_PROGRESS; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // wait while bus is high
 | ||||||
|  |     time = OWS_MSG_HIGH_TIMEOUT; | ||||||
|  |     time = onewire_slave_wait_while_gpio_is(bus, time, true); | ||||||
|  |     if(time == 0) { | ||||||
|  |         bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // choose write time
 | ||||||
|  |     if(write_zero) { | ||||||
|  |         furi_hal_ibutton_pin_low(); | ||||||
|  |         time = OWS_WRITE_ZERO; | ||||||
|  |     } else { | ||||||
|  |         time = OWS_READ_MAX; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // hold line for ZERO or ONE time
 | ||||||
|  |     delay_us(time); | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_cmd_search_rom(OneWireSlave* bus) { | ||||||
|  |     const uint8_t key_bytes = 8; | ||||||
|  |     uint8_t* key = onewire_device_get_id_p(bus->device); | ||||||
|  | 
 | ||||||
|  |     for(uint8_t i = 0; i < key_bytes; i++) { | ||||||
|  |         uint8_t key_byte = key[i]; | ||||||
|  | 
 | ||||||
|  |         for(uint8_t j = 0; j < 8; j++) { | ||||||
|  |             bool bit = (key_byte >> j) & 0x01; | ||||||
|  | 
 | ||||||
|  |             if(!onewire_slave_send_bit(bus, bit)) return; | ||||||
|  |             if(!onewire_slave_send_bit(bus, !bit)) return; | ||||||
|  | 
 | ||||||
|  |             onewire_slave_receive_bit(bus); | ||||||
|  |             if(bus->error != NO_ERROR) return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { | ||||||
|  |     uint8_t cmd; | ||||||
|  |     onewire_slave_receive(bus, &cmd, 1); | ||||||
|  | 
 | ||||||
|  |     if(bus->error == RESET_IN_PROGRESS) return true; | ||||||
|  |     if(bus->error != NO_ERROR) return false; | ||||||
|  | 
 | ||||||
|  |     switch(cmd) { | ||||||
|  |     case 0xF0: | ||||||
|  |         // SEARCH ROM
 | ||||||
|  |         onewire_slave_cmd_search_rom(bus); | ||||||
|  |         return true; | ||||||
|  | 
 | ||||||
|  |     case 0x0F: | ||||||
|  |     case 0x33: | ||||||
|  |         // READ ROM
 | ||||||
|  |         onewire_device_send_id(bus->device); | ||||||
|  |         return true; | ||||||
|  | 
 | ||||||
|  |     default: // Unknown command
 | ||||||
|  |         bus->error = INCORRECT_ONEWIRE_CMD; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(bus->error == RESET_IN_PROGRESS) return true; | ||||||
|  |     return (bus->error == NO_ERROR); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_bus_start(OneWireSlave* bus) { | ||||||
|  |     bool result = true; | ||||||
|  | 
 | ||||||
|  |     if(bus->device == NULL) { | ||||||
|  |         result = false; | ||||||
|  |     } else { | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|  |         furi_hal_ibutton_start_drive_in_isr(); | ||||||
|  |         bus->error = NO_ERROR; | ||||||
|  | 
 | ||||||
|  |         if(onewire_slave_show_presence(bus)) { | ||||||
|  |             // TODO think about multiple command cycles
 | ||||||
|  |             onewire_slave_receive_and_process_cmd(bus); | ||||||
|  |             result = (bus->error == NO_ERROR || bus->error == INCORRECT_ONEWIRE_CMD); | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             result = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         furi_hal_ibutton_start_interrupt_in_isr(); | ||||||
|  |         FURI_CRITICAL_EXIT(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void exti_cb(void* context) { | ||||||
|  |     OneWireSlave* bus = context; | ||||||
|  | 
 | ||||||
|  |     volatile bool input_state = furi_hal_ibutton_pin_get_level(); | ||||||
|  |     static uint32_t pulse_start = 0; | ||||||
|  | 
 | ||||||
|  |     if(input_state) { | ||||||
|  |         uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / instructions_per_us; | ||||||
|  |         if(pulse_length >= OWS_RESET_MIN) { | ||||||
|  |             if(pulse_length <= OWS_RESET_MAX) { | ||||||
|  |                 // reset cycle ok
 | ||||||
|  |                 bool result = onewire_slave_bus_start(bus); | ||||||
|  |                 if(result && bus->result_cb != NULL) { | ||||||
|  |                     bus->result_cb(bus->result_cb_ctx); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 bus->error = VERY_LONG_RESET; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             bus->error = VERY_SHORT_RESET; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         //FALL event
 | ||||||
|  |         pulse_start = DWT->CYCCNT; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*********************** PUBLIC ***********************/ | ||||||
|  | 
 | ||||||
|  | OneWireSlave* onewire_slave_alloc() { | ||||||
|  |     OneWireSlave* bus = malloc(sizeof(OneWireSlave)); | ||||||
|  |     bus->error = NO_ERROR; | ||||||
|  |     bus->device = NULL; | ||||||
|  |     bus->result_cb = NULL; | ||||||
|  |     bus->result_cb_ctx = NULL; | ||||||
|  |     return bus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_free(OneWireSlave* bus) { | ||||||
|  |     onewire_slave_stop(bus); | ||||||
|  |     free(bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_start(OneWireSlave* bus) { | ||||||
|  |     furi_hal_ibutton_add_interrupt(exti_cb, bus); | ||||||
|  |     furi_hal_ibutton_start_interrupt(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_stop(OneWireSlave* bus) { | ||||||
|  |     furi_hal_ibutton_stop(); | ||||||
|  |     furi_hal_ibutton_remove_interrupt(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { | ||||||
|  |     bus->device = device; | ||||||
|  |     onewire_device_attach(device, bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_detach(OneWireSlave* bus) { | ||||||
|  |     if(bus->device != NULL) { | ||||||
|  |         onewire_device_detach(bus->device); | ||||||
|  |     } | ||||||
|  |     bus->device = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void onewire_slave_set_result_callback( | ||||||
|  |     OneWireSlave* bus, | ||||||
|  |     OneWireSlaveResultCallback result_cb, | ||||||
|  |     void* context) { | ||||||
|  |     bus->result_cb = result_cb; | ||||||
|  |     bus->result_cb_ctx = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { | ||||||
|  |     uint8_t bytes_sent = 0; | ||||||
|  | 
 | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  | 
 | ||||||
|  |     // bytes loop
 | ||||||
|  |     for(; bytes_sent < data_length; ++bytes_sent) { | ||||||
|  |         const uint8_t data_byte = address[bytes_sent]; | ||||||
|  | 
 | ||||||
|  |         // bit loop
 | ||||||
|  |         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||||
|  |             if(!onewire_slave_send_bit(bus, bit_mask & data_byte)) { | ||||||
|  |                 // if we cannot send first bit
 | ||||||
|  |                 if((bit_mask == 0x01) && (bus->error == AWAIT_TIMESLOT_TIMEOUT_HIGH)) | ||||||
|  |                     bus->error = FIRST_BIT_OF_BYTE_TIMEOUT; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { | ||||||
|  |     uint8_t bytes_received = 0; | ||||||
|  | 
 | ||||||
|  |     furi_hal_ibutton_pin_high(); | ||||||
|  | 
 | ||||||
|  |     for(; bytes_received < data_length; ++bytes_received) { | ||||||
|  |         uint8_t value = 0; | ||||||
|  | 
 | ||||||
|  |         for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) { | ||||||
|  |             if(onewire_slave_receive_bit(bus)) value |= bit_mask; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         data[bytes_received] = value; | ||||||
|  |     } | ||||||
|  |     return (bytes_received != data_length); | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								lib/one_wire/one_wire_slave.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/one_wire/one_wire_slave.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file one_wire_slave.h | ||||||
|  |  *  | ||||||
|  |  * 1-Wire slave library. Currently it can only emulate ID. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <furi_hal_gpio.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct OneWireDevice OneWireDevice; | ||||||
|  | typedef struct OneWireSlave OneWireSlave; | ||||||
|  | typedef void (*OneWireSlaveResultCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate onewire slave | ||||||
|  |  * @param pin  | ||||||
|  |  * @return OneWireSlave*  | ||||||
|  |  */ | ||||||
|  | OneWireSlave* onewire_slave_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free onewire slave | ||||||
|  |  * @param bus  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_free(OneWireSlave* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start working with the bus | ||||||
|  |  * @param bus  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_start(OneWireSlave* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop working with the bus | ||||||
|  |  * @param bus  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_stop(OneWireSlave* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Attach device for emulation | ||||||
|  |  * @param bus  | ||||||
|  |  * @param device  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Detach device from bus | ||||||
|  |  * @param bus  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_detach(OneWireSlave* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set a callback to report emulation success | ||||||
|  |  * @param bus  | ||||||
|  |  * @param result_cb  | ||||||
|  |  * @param context  | ||||||
|  |  */ | ||||||
|  | void onewire_slave_set_result_callback( | ||||||
|  |     OneWireSlave* bus, | ||||||
|  |     OneWireSlaveResultCallback result_cb, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										38
									
								
								lib/one_wire/one_wire_slave_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/one_wire/one_wire_slave_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file one_wire_slave_i.h | ||||||
|  |  *  | ||||||
|  |  * 1-Wire slave library, internal functions | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct OneWireDevice OneWireDevice; | ||||||
|  | typedef struct OneWireSlave OneWireSlave; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Send data, called from emulated device | ||||||
|  |  * @param bus  | ||||||
|  |  * @param address  | ||||||
|  |  * @param data_length  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Receive data, called from emulated device | ||||||
|  |  * @param bus  | ||||||
|  |  * @param data  | ||||||
|  |  * @param data_length  | ||||||
|  |  * @return bool  | ||||||
|  |  */ | ||||||
|  | bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										66
									
								
								lib/one_wire/pulse_protocols/pulse_decoder.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								lib/one_wire/pulse_protocols/pulse_decoder.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | #include <stdlib.h> | ||||||
|  | #include "pulse_decoder.h" | ||||||
|  | #include <string.h> | ||||||
|  | #include <furi/check.h> | ||||||
|  | 
 | ||||||
|  | #define MAX_PROTOCOL 5 | ||||||
|  | 
 | ||||||
|  | struct PulseDecoder { | ||||||
|  |     PulseProtocol* protocols[MAX_PROTOCOL]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | PulseDecoder* pulse_decoder_alloc() { | ||||||
|  |     PulseDecoder* decoder = malloc(sizeof(PulseDecoder)); | ||||||
|  |     memset(decoder, 0, sizeof(PulseDecoder)); | ||||||
|  |     return decoder; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_decoder_free(PulseDecoder* reader) { | ||||||
|  |     furi_assert(reader); | ||||||
|  |     free(reader); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_decoder_add_protocol(PulseDecoder* reader, PulseProtocol* protocol, int32_t index) { | ||||||
|  |     furi_check(index < MAX_PROTOCOL); | ||||||
|  |     furi_check(reader->protocols[index] == NULL); | ||||||
|  |     reader->protocols[index] = protocol; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_decoder_process_pulse(PulseDecoder* reader, bool polarity, uint32_t length) { | ||||||
|  |     furi_assert(reader); | ||||||
|  |     for(size_t index = 0; index < MAX_PROTOCOL; index++) { | ||||||
|  |         if(reader->protocols[index] != NULL) { | ||||||
|  |             pulse_protocol_process_pulse(reader->protocols[index], polarity, length); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t pulse_decoder_get_decoded_index(PulseDecoder* reader) { | ||||||
|  |     furi_assert(reader); | ||||||
|  |     int32_t decoded = -1; | ||||||
|  |     for(size_t index = 0; index < MAX_PROTOCOL; index++) { | ||||||
|  |         if(reader->protocols[index] != NULL) { | ||||||
|  |             if(pulse_protocol_decoded(reader->protocols[index])) { | ||||||
|  |                 decoded = index; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return decoded; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_decoder_reset(PulseDecoder* reader) { | ||||||
|  |     furi_assert(reader); | ||||||
|  |     for(size_t index = 0; index < MAX_PROTOCOL; index++) { | ||||||
|  |         if(reader->protocols[index] != NULL) { | ||||||
|  |             pulse_protocol_reset(reader->protocols[index]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_decoder_get_data(PulseDecoder* reader, int32_t index, uint8_t* data, size_t length) { | ||||||
|  |     furi_assert(reader); | ||||||
|  |     furi_check(reader->protocols[index] != NULL); | ||||||
|  |     pulse_protocol_get_data(reader->protocols[index], data, length); | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								lib/one_wire/pulse_protocols/pulse_decoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/one_wire/pulse_protocols/pulse_decoder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file pulse_decoder.h | ||||||
|  |  *  | ||||||
|  |  * Generic pulse protocol decoder library | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include "pulse_protocol.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct PulseDecoder PulseDecoder; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate decoder | ||||||
|  |  * @return PulseDecoder*  | ||||||
|  |  */ | ||||||
|  | PulseDecoder* pulse_decoder_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate decoder | ||||||
|  |  * @param decoder  | ||||||
|  |  */ | ||||||
|  | void pulse_decoder_free(PulseDecoder* decoder); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Add protocol to decoder | ||||||
|  |  * @param decoder  | ||||||
|  |  * @param protocol protocol implementation | ||||||
|  |  * @param index protocol index, should not be repeated | ||||||
|  |  */ | ||||||
|  | void pulse_decoder_add_protocol(PulseDecoder* decoder, PulseProtocol* protocol, int32_t index); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Push and process pulse with decoder | ||||||
|  |  * @param decoder  | ||||||
|  |  * @param polarity  | ||||||
|  |  * @param length  | ||||||
|  |  */ | ||||||
|  | void pulse_decoder_process_pulse(PulseDecoder* decoder, bool polarity, uint32_t length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get indec of decoded protocol | ||||||
|  |  * @param decoder  | ||||||
|  |  * @return int32_t, -1 if nothing decoded, or index of decoded protocol  | ||||||
|  |  */ | ||||||
|  | int32_t pulse_decoder_get_decoded_index(PulseDecoder* decoder); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset all protocols in decoder | ||||||
|  |  * @param decoder  | ||||||
|  |  */ | ||||||
|  | void pulse_decoder_reset(PulseDecoder* decoder); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get decoded data from protocol | ||||||
|  |  * @param decoder  | ||||||
|  |  * @param index  | ||||||
|  |  * @param data  | ||||||
|  |  * @param length  | ||||||
|  |  */ | ||||||
|  | void pulse_decoder_get_data(PulseDecoder* decoder, int32_t index, uint8_t* data, size_t length); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										55
									
								
								lib/one_wire/pulse_protocols/pulse_glue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								lib/one_wire/pulse_protocols/pulse_glue.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #include "pulse_glue.h" | ||||||
|  | 
 | ||||||
|  | struct PulseGlue { | ||||||
|  |     int32_t hi_period; | ||||||
|  |     int32_t low_period; | ||||||
|  |     int32_t next_hi_period; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | PulseGlue* pulse_glue_alloc() { | ||||||
|  |     PulseGlue* pulse_glue = malloc(sizeof(PulseGlue)); | ||||||
|  |     pulse_glue_reset(pulse_glue); | ||||||
|  |     return pulse_glue; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_glue_free(PulseGlue* pulse_glue) { | ||||||
|  |     free(pulse_glue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_glue_reset(PulseGlue* pulse_glue) { | ||||||
|  |     pulse_glue->hi_period = 0; | ||||||
|  |     pulse_glue->low_period = 0; | ||||||
|  |     pulse_glue->next_hi_period = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length) { | ||||||
|  |     bool pop_ready = false; | ||||||
|  |     if(polarity) { | ||||||
|  |         if(pulse_glue->low_period == 0) { | ||||||
|  |             // stage 1, accumulate hi period
 | ||||||
|  |             pulse_glue->hi_period += length; | ||||||
|  |         } else { | ||||||
|  |             // stage 3, accumulate next hi period and be ready for pulse_glue_pop
 | ||||||
|  |             pulse_glue->next_hi_period = length; | ||||||
|  | 
 | ||||||
|  |             // data is ready
 | ||||||
|  |             pop_ready = true; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(pulse_glue->hi_period != 0) { | ||||||
|  |             // stage 2, accumulate low period
 | ||||||
|  |             pulse_glue->low_period += length; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return pop_ready; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period) { | ||||||
|  |     *length = pulse_glue->hi_period + pulse_glue->low_period; | ||||||
|  |     *period = pulse_glue->hi_period; | ||||||
|  | 
 | ||||||
|  |     pulse_glue->hi_period = pulse_glue->next_hi_period; | ||||||
|  |     pulse_glue->low_period = 0; | ||||||
|  |     pulse_glue->next_hi_period = 0; | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								lib/one_wire/pulse_protocols/pulse_glue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/one_wire/pulse_protocols/pulse_glue.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file pulse_glue.h | ||||||
|  |  *  | ||||||
|  |  * Simple tool to glue separated pulses to corret  | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct PulseGlue PulseGlue; | ||||||
|  | 
 | ||||||
|  | PulseGlue* pulse_glue_alloc(); | ||||||
|  | void pulse_glue_free(PulseGlue* pulse_glue); | ||||||
|  | void pulse_glue_reset(PulseGlue* pulse_glue); | ||||||
|  | 
 | ||||||
|  | bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length); | ||||||
|  | void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										67
									
								
								lib/one_wire/pulse_protocols/pulse_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								lib/one_wire/pulse_protocols/pulse_protocol.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | #include "pulse_protocol.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | struct PulseProtocol { | ||||||
|  |     void* context; | ||||||
|  |     PulseProtocolPulseCallback pulse_cb; | ||||||
|  |     PulseProtocolResetCallback reset_cb; | ||||||
|  |     PulseProtocolGetDataCallback get_data_cb; | ||||||
|  |     PulseProtocolDecodedCallback decoded_cb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | PulseProtocol* pulse_protocol_alloc() { | ||||||
|  |     PulseProtocol* protocol = malloc(sizeof(PulseProtocol)); | ||||||
|  |     memset(protocol, 0, sizeof(PulseProtocol)); | ||||||
|  |     return protocol; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_set_context(PulseProtocol* protocol, void* context) { | ||||||
|  |     protocol->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback) { | ||||||
|  |     protocol->pulse_cb = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback) { | ||||||
|  |     protocol->reset_cb = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback) { | ||||||
|  |     protocol->get_data_cb = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback) { | ||||||
|  |     protocol->decoded_cb = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_free(PulseProtocol* protocol) { | ||||||
|  |     free(protocol); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length) { | ||||||
|  |     if(protocol->pulse_cb != NULL) { | ||||||
|  |         protocol->pulse_cb(protocol->context, polarity, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_reset(PulseProtocol* protocol) { | ||||||
|  |     if(protocol->reset_cb != NULL) { | ||||||
|  |         protocol->reset_cb(protocol->context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool pulse_protocol_decoded(PulseProtocol* protocol) { | ||||||
|  |     bool result = false; | ||||||
|  |     if(protocol->decoded_cb != NULL) { | ||||||
|  |         result = protocol->decoded_cb(protocol->context); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length) { | ||||||
|  |     if(protocol->get_data_cb != NULL) { | ||||||
|  |         protocol->get_data_cb(protocol->context, data, length); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										122
									
								
								lib/one_wire/pulse_protocols/pulse_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								lib/one_wire/pulse_protocols/pulse_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file pulse_protocol.h | ||||||
|  |  *  | ||||||
|  |  * Generic pulse protocol decoder library, protocol interface | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Anonymous PulseProtocol struct | ||||||
|  |  */ | ||||||
|  | typedef struct PulseProtocol PulseProtocol; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Process pulse callback | ||||||
|  |  */ | ||||||
|  | typedef void (*PulseProtocolPulseCallback)(void* context, bool polarity, uint32_t length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reset protocol callback | ||||||
|  |  */ | ||||||
|  | typedef void (*PulseProtocolResetCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get decoded data callback | ||||||
|  |  */ | ||||||
|  | typedef void (*PulseProtocolGetDataCallback)(void* context, uint8_t* data, size_t length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Is protocol decoded callback | ||||||
|  |  */ | ||||||
|  | typedef bool (*PulseProtocolDecodedCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate protocol | ||||||
|  |  * @return PulseProtocol*  | ||||||
|  |  */ | ||||||
|  | PulseProtocol* pulse_protocol_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deallocate protocol | ||||||
|  |  * @param protocol  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_free(PulseProtocol* protocol); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set context for callbacks | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param context  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_set_context(PulseProtocol* protocol, void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "Process pulse" callback. Called from the decoder when a new pulse is received. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param callback  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "Reset protocol" callback. Called from the decoder when the decoder is reset. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param callback  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "Get decoded data" callback. Called from the decoder when the decoder wants to get decoded data. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param callback  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set "Is protocol decoded" callback. Called from the decoder when the decoder wants to know if a protocol has been decoded. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param callback  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Part of decoder interface. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @param polarity  | ||||||
|  |  * @param length  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Part of decoder interface. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | bool pulse_protocol_decoded(PulseProtocol* protocol); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Part of decoder interface. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Part of decoder interface. | ||||||
|  |  * @param protocol  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | void pulse_protocol_reset(PulseProtocol* protocol); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG