[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; | ||||
| 
 | ||||
|     wiegand.begin(); | ||||
|     onewire_master.start(); | ||||
|     onewire_host_start(onewire_host); | ||||
| 
 | ||||
|     scenes[current_scene]->on_enter(this); | ||||
| 
 | ||||
| @ -28,19 +28,20 @@ void AccessorApp::run(void) { | ||||
|     scenes[current_scene]->on_exit(this); | ||||
| 
 | ||||
|     wiegand.end(); | ||||
|     onewire_master.stop(); | ||||
|     onewire_host_stop(onewire_host); | ||||
| } | ||||
| 
 | ||||
| AccessorApp::AccessorApp() | ||||
|     : onewire_master{&ibutton_gpio} { | ||||
| AccessorApp::AccessorApp() { | ||||
|     furi_hal_power_insomnia_enter(); | ||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||
|     onewire_host = onewire_host_alloc(); | ||||
|     furi_hal_power_enable_otg(); | ||||
| } | ||||
| 
 | ||||
| AccessorApp::~AccessorApp() { | ||||
|     furi_hal_power_disable_otg(); | ||||
|     furi_record_close("notification"); | ||||
|     onewire_host_free(onewire_host); | ||||
|     furi_hal_power_insomnia_exit(); | ||||
| } | ||||
| 
 | ||||
| @ -136,6 +137,6 @@ WIEGAND* AccessorApp::get_wiegand() { | ||||
|     return &wiegand; | ||||
| } | ||||
| 
 | ||||
| OneWireMaster* AccessorApp::get_one_wire() { | ||||
|     return &onewire_master; | ||||
| OneWireHost* AccessorApp::get_one_wire() { | ||||
|     return onewire_host; | ||||
| } | ||||
| @ -2,13 +2,9 @@ | ||||
| #include <map> | ||||
| #include <list> | ||||
| #include "accessor_view_manager.h" | ||||
| 
 | ||||
| #include "scene/accessor_scene_start.h" | ||||
| 
 | ||||
| #include "helpers/wiegand.h" | ||||
| 
 | ||||
| #include <one_wire_master.h> | ||||
| 
 | ||||
| #include <one_wire/one_wire_host.h> | ||||
| #include <notification/notification_messages.h> | ||||
| 
 | ||||
| class AccessorApp { | ||||
| @ -37,7 +33,7 @@ public: | ||||
|     void set_text_store(const char* text...); | ||||
| 
 | ||||
|     WIEGAND* get_wiegand(); | ||||
|     OneWireMaster* get_one_wire(); | ||||
|     OneWireHost* get_one_wire(); | ||||
| 
 | ||||
| private: | ||||
|     std::list<Scene> previous_scenes_list = {Scene::Exit}; | ||||
| @ -52,7 +48,7 @@ private: | ||||
|     char text_store[text_store_size + 1]; | ||||
| 
 | ||||
|     WIEGAND wiegand; | ||||
|     OneWireMaster onewire_master; | ||||
|     OneWireHost* onewire_host; | ||||
| 
 | ||||
|     NotificationApp* notification; | ||||
| }; | ||||
| @ -21,7 +21,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | ||||
|     if(event->type == AccessorEvent::Type::Tick) { | ||||
|         WIEGAND* wiegand = app->get_wiegand(); | ||||
|         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 type = 0; | ||||
| @ -38,11 +38,11 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | ||||
|             } | ||||
|         } else { | ||||
|             FURI_CRITICAL_ENTER(); | ||||
|             if(onewire->reset()) { | ||||
|             if(onewire_host_reset(onewire_host)) { | ||||
|                 type = 255; | ||||
|                 onewire->write(0x33); | ||||
|                 onewire_host_write(onewire_host, 0x33); | ||||
|                 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++) { | ||||
|  | ||||
| @ -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 <stdarg.h> | ||||
| #include <callback-connector.h> | ||||
| #include <furi_hal.h> | ||||
| #include <m-string.h> | ||||
| #include <toolbox/path.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| @ -42,7 +42,9 @@ iButtonApp::iButtonApp() | ||||
|     , storage{"storage"} | ||||
|     , dialogs{"dialogs"} { | ||||
|     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() { | ||||
| @ -50,7 +52,10 @@ iButtonApp::~iButtonApp() { | ||||
|         delete it->second; | ||||
|     } | ||||
|     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(); | ||||
| } | ||||
| @ -112,17 +117,12 @@ iButtonApp::Scene iButtonApp::get_previous_scene() { | ||||
|     return scene; | ||||
| } | ||||
| 
 | ||||
| const GpioPin* iButtonApp::get_ibutton_pin() { | ||||
|     // TODO open record
 | ||||
|     return &ibutton_gpio; | ||||
| } | ||||
| 
 | ||||
| KeyWorker* iButtonApp::get_key_worker() { | ||||
| iButtonWorker* iButtonApp::get_key_worker() { | ||||
|     return key_worker; | ||||
| } | ||||
| 
 | ||||
| iButtonKey* iButtonApp::get_key() { | ||||
|     return &key; | ||||
|     return key; | ||||
| } | ||||
| 
 | ||||
| char* iButtonApp::get_file_name() { | ||||
| @ -201,10 +201,11 @@ bool iButtonApp::save_key(const char* key_name) { | ||||
|         if(!delete_key()) break; | ||||
| 
 | ||||
|         // Save the key
 | ||||
|         key.set_name(key_name); | ||||
|         ibutton_key_set_name(key, key_name); | ||||
| 
 | ||||
|         // 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
 | ||||
|         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
 | ||||
|         if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom")) | ||||
|             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; | ||||
| 
 | ||||
|         // 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")) | ||||
|             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; | ||||
|         result = true; | ||||
| 
 | ||||
| @ -258,15 +260,15 @@ bool iButtonApp::load_key_data(string_t key_path) { | ||||
|         // key type
 | ||||
|         iButtonKeyType type; | ||||
|         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
 | ||||
|         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; | ||||
| 
 | ||||
|         key.set_type(type); | ||||
|         key.set_data(key_data, IBUTTON_KEY_DATA_SIZE); | ||||
|         ibutton_key_set_type(key, type); | ||||
|         ibutton_key_set_data(key, key_data, IBUTTON_KEY_DATA_SIZE); | ||||
| 
 | ||||
|         result = true; | ||||
|     } while(false); | ||||
| @ -290,7 +292,7 @@ bool iButtonApp::load_key(const char* key_name) { | ||||
|     result = load_key_data(key_path); | ||||
|     if(result) { | ||||
|         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); | ||||
|     return result; | ||||
| @ -306,7 +308,7 @@ bool iButtonApp::load_key() { | ||||
|         app_extension, | ||||
|         get_file_name(), | ||||
|         get_file_name_size(), | ||||
|         get_key()->get_name()); | ||||
|         ibutton_key_get_name_p(key)); | ||||
| 
 | ||||
|     if(res) { | ||||
|         string_t key_str; | ||||
| @ -316,7 +318,7 @@ bool iButtonApp::load_key() { | ||||
| 
 | ||||
|         result = load_key_data(key_str); | ||||
|         if(result) { | ||||
|             get_key()->set_name(get_file_name()); | ||||
|             ibutton_key_set_name(key, get_file_name()); | ||||
|         } | ||||
|         string_clear(key_str); | ||||
|     } | ||||
| @ -328,7 +330,8 @@ bool iButtonApp::delete_key() { | ||||
|     string_t file_name; | ||||
|     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)); | ||||
|     string_clear(file_name); | ||||
| 
 | ||||
|  | ||||
| @ -22,17 +22,10 @@ | ||||
| #include "scene/ibutton_scene_select_key.h" | ||||
| #include "scene/ibutton_scene_add_type.h" | ||||
| #include "scene/ibutton_scene_add_value.h" | ||||
| 
 | ||||
| #include "helpers/key_worker.h" | ||||
| 
 | ||||
| #include "one_wire_master.h" | ||||
| #include "maxim_crc.h" | ||||
| #include "ibutton_key.h" | ||||
| 
 | ||||
| #include <one_wire/ibutton/ibutton_worker.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <storage/storage.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include <record_controller.hpp> | ||||
| 
 | ||||
| class iButtonApp { | ||||
| @ -75,7 +68,7 @@ public: | ||||
|     Scene get_previous_scene(); | ||||
| 
 | ||||
|     const GpioPin* get_ibutton_pin(); | ||||
|     KeyWorker* get_key_worker(); | ||||
|     iButtonWorker* get_key_worker(); | ||||
|     iButtonKey* get_key(); | ||||
| 
 | ||||
|     void notify_green_blink(); | ||||
| @ -127,9 +120,8 @@ private: | ||||
|         {Scene::SceneAddValue, new iButtonSceneAddValue()}, | ||||
|     }; | ||||
| 
 | ||||
|     KeyWorker* key_worker; | ||||
| 
 | ||||
|     iButtonKey key; | ||||
|     iButtonWorker* key_worker; | ||||
|     iButtonKey* key; | ||||
| 
 | ||||
|     RecordController<NotificationApp> notification; | ||||
|     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 <gui/modules/dialog_ex.h> | ||||
| #include <gui/modules/widget.h> | ||||
| #include <one_wire/ibutton/ibutton_worker.h> | ||||
| 
 | ||||
| class iButtonApp; | ||||
| 
 | ||||
| @ -16,6 +17,9 @@ public: | ||||
|         EventTypeTextEditResult, | ||||
|         EventTypeByteEditResult, | ||||
|         EventTypeWidgetButtonResult, | ||||
|         EventTypeWorkerEmulated, | ||||
|         EventTypeWorkerRead, | ||||
|         EventTypeWorkerWrite, | ||||
|     }; | ||||
| 
 | ||||
|     // payload
 | ||||
| @ -23,6 +27,7 @@ public: | ||||
|         uint32_t menu_index; | ||||
|         DialogExResult dialog_result; | ||||
|         GuiButtonType widget_button_result; | ||||
|         iButtonWorkerWriteResult worker_write_result; | ||||
|     } payload; | ||||
| 
 | ||||
|     // 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -10,14 +8,23 @@ typedef enum { | ||||
|     SubmenuIndexMetakom, | ||||
| } 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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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, "Dallas", SubmenuIndexDallas, callback, app); | ||||
|     submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, callback, app); | ||||
|     submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
| 
 | ||||
|     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) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         iButtonKey* key = app->get_key(); | ||||
| 
 | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexCyfral: | ||||
|             app->get_key()->set_type(iButtonKeyType::KeyCyfral); | ||||
|             ibutton_key_set_type(key, iButtonKeyCyfral); | ||||
|             break; | ||||
|         case SubmenuIndexDallas: | ||||
|             app->get_key()->set_type(iButtonKeyType::KeyDallas); | ||||
|             ibutton_key_set_type(key, iButtonKeyDS1990); | ||||
|             break; | ||||
|         case SubmenuIndexMetakom: | ||||
|             app->get_key()->set_type(iButtonKeyType::KeyMetakom); | ||||
|             ibutton_key_set_type(key, iButtonKeyMetakom); | ||||
|             break; | ||||
|         } | ||||
|         app->get_key()->set_name(""); | ||||
|         app->get_key()->clear_data(); | ||||
|         ibutton_key_set_name(key, ""); | ||||
|         ibutton_key_clear_data(key); | ||||
|         app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue); | ||||
|         consumed = true; | ||||
|     } | ||||
| @ -54,13 +63,3 @@ void iButtonSceneAddType::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
| private: | ||||
|     void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| @ -1,17 +1,30 @@ | ||||
| #include "ibutton_scene_add_value.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ByteInput* byte_input = view_manager->get_byte_input(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneAddValue::byte_input_callback); | ||||
|     memcpy(this->new_key_data, app->get_key()->get_data(), app->get_key()->get_type_data_size()); | ||||
|     iButtonKey* key = app->get_key(); | ||||
| 
 | ||||
|     memcpy(this->new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)); | ||||
| 
 | ||||
|     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"); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput); | ||||
| @ -21,6 +34,7 @@ bool iButtonSceneAddValue::on_event(iButtonApp* app, iButtonEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == iButtonEvent::Type::EventTypeByteEditResult) { | ||||
|         ibutton_key_set_data(app->get_key(), this->new_key_data, IBUTTON_KEY_DATA_SIZE); | ||||
|         DOLPHIN_DEED(DolphinDeedIbuttonAdd); | ||||
|         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); | ||||
|         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_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 | ||||
| #include "ibutton_scene_generic.h" | ||||
| #include "../ibutton_key.h" | ||||
| #include <one_wire/ibutton/ibutton_key.h> | ||||
| 
 | ||||
| class iButtonSceneAddValue : public iButtonScene { | ||||
| public: | ||||
| @ -9,6 +9,5 @@ public: | ||||
|     void on_exit(iButtonApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     void byte_input_callback(void* context); | ||||
|     uint8_t new_key_data[IBUTTON_KEY_DATA_SIZE] = {}; | ||||
| }; | ||||
| @ -1,25 +1,31 @@ | ||||
| #include "ibutton_scene_delete_confirm.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| static void 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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Widget* widget = view_manager->get_widget(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteConfirm::widget_callback); | ||||
| 
 | ||||
|     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, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); | ||||
|     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", callback, app); | ||||
|     widget_add_button_element(widget, GuiButtonTypeRight, "Delete", callback, app); | ||||
|     widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app); | ||||
|     widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); | ||||
| 
 | ||||
|     switch(key->get_key_type()) { | ||||
|     case iButtonKeyType::KeyDallas: | ||||
|     switch(ibutton_key_get_type(key)) { | ||||
|     case iButtonKeyDS1990: | ||||
|         app->set_text_store( | ||||
|             "%02X %02X %02X %02X %02X %02X %02X %02X", | ||||
|             key_data[0], | ||||
| @ -33,12 +39,12 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | ||||
|         widget_add_string_element( | ||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyCyfral: | ||||
|     case iButtonKeyCyfral: | ||||
|         app->set_text_store("%02X %02X", key_data[0], key_data[1]); | ||||
|         widget_add_string_element( | ||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyMetakom: | ||||
|     case iButtonKeyMetakom: | ||||
|         app->set_text_store( | ||||
|             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||
|         widget_add_string_element( | ||||
| @ -77,18 +83,3 @@ void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.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 iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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_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_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| @ -42,10 +44,3 @@ void iButtonSceneDeleteSuccess::on_exit(iButtonApp* app) { | ||||
|     popup_set_context(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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||
|     void on_exit(iButtonApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     void popup_callback(void* context); | ||||
| }; | ||||
| @ -1,17 +1,21 @@ | ||||
| #include "ibutton_scene_emulate.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     uint8_t* key_data = key->get_data(); | ||||
|     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; | ||||
|     DOLPHIN_DEED(DolphinDeedIbuttonEmulate); | ||||
| 
 | ||||
| @ -21,8 +25,8 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | ||||
|         line_count = 2; | ||||
|     } else { | ||||
|         // if not, show key data
 | ||||
|         switch(key->get_key_type()) { | ||||
|         case iButtonKeyType::KeyDallas: | ||||
|         switch(ibutton_key_get_type(key)) { | ||||
|         case iButtonKeyDS1990: | ||||
|             app->set_text_store( | ||||
|                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||
|                 key_data[0], | ||||
| @ -35,11 +39,11 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | ||||
|                 key_data[7]); | ||||
|             line_count = 3; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyCyfral: | ||||
|         case iButtonKeyCyfral: | ||||
|             app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]); | ||||
|             line_count = 2; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyMetakom: | ||||
|         case iButtonKeyMetakom: | ||||
|             app->set_text_store( | ||||
|                 "emulating\n%02X %02X %02X %02X", | ||||
|                 key_data[0], | ||||
| @ -66,29 +70,28 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) { | ||||
|     popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44); | ||||
| 
 | ||||
|     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 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; | ||||
|         if(app->get_key_worker()->emulated()) { | ||||
|             app->notify_yellow_blink(); | ||||
|         } else { | ||||
|             app->notify_red_blink(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void iButtonSceneEmulate::on_exit(iButtonApp* app) { | ||||
|     app->get_key_worker()->stop_emulate(); | ||||
| 
 | ||||
|     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_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| #pragma once | ||||
| #include "ibutton_scene_generic.h" | ||||
| #include "../helpers/key_emulator.h" | ||||
| 
 | ||||
| class iButtonSceneEmulate : public iButtonScene { | ||||
| public: | ||||
|  | ||||
| @ -1,24 +1,30 @@ | ||||
| #include "ibutton_scene_info.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| static void 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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void iButtonSceneInfo::on_enter(iButtonApp* app) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Widget* widget = view_manager->get_widget(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneInfo::widget_callback); | ||||
| 
 | ||||
|     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, 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()) { | ||||
|     case iButtonKeyType::KeyDallas: | ||||
|     switch(ibutton_key_get_type(key)) { | ||||
|     case iButtonKeyDS1990: | ||||
|         app->set_text_store( | ||||
|             "%02X %02X %02X %02X %02X %02X %02X %02X", | ||||
|             key_data[0], | ||||
| @ -32,13 +38,13 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) { | ||||
|         widget_add_string_element( | ||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas"); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyMetakom: | ||||
|     case iButtonKeyMetakom: | ||||
|         app->set_text_store( | ||||
|             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||
|         widget_add_string_element( | ||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom"); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyCyfral: | ||||
|     case iButtonKeyCyfral: | ||||
|         app->set_text_store("%02X %02X", key_data[0], key_data[1]); | ||||
|         widget_add_string_element( | ||||
|             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); | ||||
| @ -71,15 +77,3 @@ void iButtonSceneInfo::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     iButtonWorker* worker = app->get_key_worker(); | ||||
|     DOLPHIN_DEED(DolphinDeedIbuttonRead); | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|     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 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; | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| void iButtonSceneRead::on_exit(iButtonApp* app) { | ||||
|     app->get_key_worker()->stop_read(); | ||||
| 
 | ||||
|     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_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|  | ||||
| @ -6,6 +6,4 @@ public: | ||||
|     void on_enter(iButtonApp* app) final; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||
|     void on_exit(iButtonApp* app) final; | ||||
| 
 | ||||
| private: | ||||
| }; | ||||
| @ -1,16 +1,21 @@ | ||||
| #include "ibutton_scene_read_crc_error.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| #include <one_wire/maxim_crc.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadCRCError::dialog_ex_callback); | ||||
| 
 | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     uint8_t* key_data = key->get_data(); | ||||
|     const uint8_t* key_data = ibutton_key_get_data_p(app->get_key()); | ||||
| 
 | ||||
|     app->set_text_store( | ||||
|         "%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[6], | ||||
|         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_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     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); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||
| @ -66,13 +71,3 @@ void iButtonSceneReadCRCError::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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 | ||||
| #include "ibutton_scene_generic.h" | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| class iButtonSceneReadCRCError : public iButtonScene { | ||||
| public: | ||||
|     void on_enter(iButtonApp* app) final; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| #include <one_wire/maxim_crc.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadNotKeyError::dialog_ex_callback); | ||||
| 
 | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     uint8_t* key_data = key->get_data(); | ||||
|     const uint8_t* key_data = ibutton_key_get_data_p(app->get_key()); | ||||
| 
 | ||||
|     app->set_text_store( | ||||
|         "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[6], | ||||
|         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_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop); | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     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); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||
| @ -66,13 +71,3 @@ void iButtonSceneReadNotKeyError::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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 | ||||
| #include "ibutton_scene_generic.h" | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| class iButtonSceneReadNotKeyError : public iButtonScene { | ||||
| public: | ||||
|     void on_enter(iButtonApp* app) final; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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); | ||||
| 
 | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     uint8_t* key_data = key->get_data(); | ||||
| 
 | ||||
|     switch(key->get_key_type()) { | ||||
|     case iButtonKeyType::KeyDallas: | ||||
|     switch(ibutton_key_get_type(key)) { | ||||
|     case iButtonKeyDS1990: | ||||
|         app->set_text_store( | ||||
|             "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||
|             key_data[0], | ||||
| @ -27,10 +32,10 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | ||||
|             key_data[6], | ||||
|             key_data[7]); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyCyfral: | ||||
|     case iButtonKeyCyfral: | ||||
|         app->set_text_store("Cyfral\n%02X %02X", key_data[0], key_data[1]); | ||||
|         break; | ||||
|     case iButtonKeyType::KeyMetakom: | ||||
|     case iButtonKeyMetakom: | ||||
|         app->set_text_store( | ||||
|             "Metakom\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||
|         break; | ||||
| @ -40,7 +45,7 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) { | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||
|     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); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx); | ||||
| @ -80,13 +85,3 @@ void iButtonSceneReadSuccess::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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 | ||||
| #include "ibutton_scene_generic.h" | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| class iButtonSceneReadSuccess : public iButtonScene { | ||||
| public: | ||||
|     void on_enter(iButtonApp* app) final; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexWrite, | ||||
| @ -11,17 +8,26 @@ typedef enum { | ||||
|     SubmenuIndexReadNewKey, | ||||
| } 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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback); | ||||
| 
 | ||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||
|     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||
|     } | ||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, callback, app); | ||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||
| @ -61,13 +67,3 @@ void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
| private: | ||||
|     void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| @ -1,18 +1,21 @@ | ||||
| #include "ibutton_scene_save_name.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> | ||||
| 
 | ||||
| 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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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 = key->get_name(); | ||||
|     const char* key_name = ibutton_key_get_name_p(app->get_key()); | ||||
|     bool key_name_empty = !strcmp(key_name, ""); | ||||
| 
 | ||||
|     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_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 = | ||||
|         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); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSaveSuccess::popup_callback); | ||||
|     DOLPHIN_DEED(DolphinDeedIbuttonSave); | ||||
| 
 | ||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||
|     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_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| @ -47,10 +49,3 @@ void iButtonSceneSaveSuccess::on_exit(iButtonApp* app) { | ||||
|     popup_set_context(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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) 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_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -12,18 +10,27 @@ typedef enum { | ||||
|     SubmenuIndexInfo, | ||||
| } 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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); | ||||
|     if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||
|     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||
|     } | ||||
|     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); | ||||
|     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); | ||||
|     submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); | ||||
|     submenu_add_item(submenu, "Edit", SubmenuIndexEdit, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Delete", SubmenuIndexDelete, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Info", SubmenuIndexInfo, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||
| @ -63,13 +70,3 @@ void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
| private: | ||||
|     void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| @ -1,7 +1,5 @@ | ||||
| #include "ibutton_scene_select_key.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.h" | ||||
| 
 | ||||
| void iButtonSceneSelectKey::on_enter(iButtonApp* app) { | ||||
|     // Process file_select return
 | ||||
|  | ||||
| @ -1,8 +1,5 @@ | ||||
| #include "ibutton_scene_start.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexRead, | ||||
| @ -10,14 +7,23 @@ typedef enum { | ||||
|     SubmenuIndexAdd, | ||||
| } 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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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, "Saved", SubmenuIndexSaved, callback, app); | ||||
|     submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, callback, app); | ||||
|     submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||
| @ -51,13 +57,3 @@ void iButtonSceneStart::on_exit(iButtonApp* app) { | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
| private: | ||||
|     void submenu_callback(void* context, uint32_t index); | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| @ -1,15 +1,22 @@ | ||||
| #include "ibutton_scene_write.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.h" | ||||
| 
 | ||||
| static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | ||||
|     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) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     iButtonKey* key = app->get_key(); | ||||
|     uint8_t* key_data = key->get_data(); | ||||
|     const char* key_name = key->get_name(); | ||||
|     iButtonWorker* worker = app->get_key_worker(); | ||||
|     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; | ||||
| 
 | ||||
|     // check that stored key has name
 | ||||
| @ -18,8 +25,8 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | ||||
|         line_count = 2; | ||||
|     } else { | ||||
|         // if not, show key data
 | ||||
|         switch(key->get_key_type()) { | ||||
|         case iButtonKeyType::KeyDallas: | ||||
|         switch(ibutton_key_get_type(key)) { | ||||
|         case iButtonKeyDS1990: | ||||
|             app->set_text_store( | ||||
|                 "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X", | ||||
|                 key_data[0], | ||||
| @ -32,11 +39,11 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | ||||
|                 key_data[7]); | ||||
|             line_count = 3; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyCyfral: | ||||
|         case iButtonKeyCyfral: | ||||
|             app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]); | ||||
|             line_count = 2; | ||||
|             break; | ||||
|         case iButtonKeyType::KeyMetakom: | ||||
|         case iButtonKeyMetakom: | ||||
|             app->set_text_store( | ||||
|                 "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]); | ||||
|             line_count = 2; | ||||
| @ -60,27 +67,34 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) { | ||||
| 
 | ||||
|     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 consumed = false; | ||||
| 
 | ||||
|     if(event->type == iButtonEvent::Type::EventTypeTick) { | ||||
|     if(event->type == iButtonEvent::Type::EventTypeWorkerWrite) { | ||||
|         consumed = true; | ||||
|         KeyWriter::Error result = app->get_key_worker()->write(app->get_key()); | ||||
| 
 | ||||
|         switch(result) { | ||||
|         case KeyWriter::Error::SAME_KEY: | ||||
|         case KeyWriter::Error::OK: | ||||
|         switch(event->payload.worker_write_result) { | ||||
|         case iButtonWorkerWriteOK: | ||||
|         case iButtonWorkerWriteSameKey: | ||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess); | ||||
|             break; | ||||
|         case KeyWriter::Error::NO_DETECT: | ||||
|             app->notify_red_blink(); | ||||
|         case iButtonWorkerWriteNoDetect: | ||||
|             blink_yellow = false; | ||||
|             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(); | ||||
|             break; | ||||
|         } else { | ||||
|             app->notify_red_blink(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -89,10 +103,8 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) { | ||||
| 
 | ||||
| void iButtonSceneWrite::on_exit(iButtonApp* app) { | ||||
|     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_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
| 
 | ||||
|     app->get_key_worker()->stop_write(); | ||||
| } | ||||
| @ -8,4 +8,5 @@ public: | ||||
|     void on_exit(iButtonApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     bool blink_yellow; | ||||
| }; | ||||
| @ -1,19 +1,22 @@ | ||||
| #include "ibutton_scene_write_success.h" | ||||
| #include "../ibutton_app.h" | ||||
| #include "../ibutton_view_manager.h" | ||||
| #include "../ibutton_event.h" | ||||
| #include "../ibutton_key.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); | ||||
|     app->notify_green_off(); | ||||
| } | ||||
| 
 | ||||
| void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     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_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_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| @ -46,11 +49,3 @@ void iButtonSceneWriteSuccess::on_exit(iButtonApp* app) { | ||||
|     popup_set_context(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; | ||||
|     bool on_event(iButtonApp* app, iButtonEvent* event) final; | ||||
|     void on_exit(iButtonApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     void popup_callback(void* context); | ||||
| }; | ||||
| @ -4,20 +4,19 @@ | ||||
| #include <cmsis_os2.h> | ||||
| 
 | ||||
| #define TAG "FuriHalDelay" | ||||
| 
 | ||||
| static uint32_t clk_per_microsecond; | ||||
| uint32_t instructions_per_us; | ||||
| 
 | ||||
| void furi_hal_delay_init(void) { | ||||
|     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; | ||||
|     DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; | ||||
|     DWT->CYCCNT = 0U; | ||||
|     clk_per_microsecond = SystemCoreClock / 1000000.0f; | ||||
|     instructions_per_us = SystemCoreClock / 1000000.0f; | ||||
|     FURI_LOG_I(TAG, "Init OK"); | ||||
| } | ||||
| 
 | ||||
| void delay_us(float microseconds) { | ||||
|     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) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| #include <furi_hal_resources.h> | ||||
| 
 | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <stm32wbxx_ll_exti.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(); | ||||
|     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() { | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     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() { | ||||
|     hal_gpio_write(&ibutton_gpio, false); | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,13 @@ typedef struct { | ||||
| 
 | ||||
| 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() { | ||||
|     furi_assert(furi_hal_rfid == NULL); | ||||
|     furi_hal_rfid = malloc(sizeof(FuriHalRfid)); | ||||
| @ -72,7 +79,7 @@ void furi_hal_rfid_pins_reset() { | ||||
| 
 | ||||
| void furi_hal_rfid_pins_emulate() { | ||||
|     // ibutton low
 | ||||
|     furi_hal_ibutton_start(); | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_ibutton_pin_low(); | ||||
| 
 | ||||
|     // pull pin to timer out
 | ||||
| @ -89,7 +96,7 @@ void furi_hal_rfid_pins_emulate() { | ||||
| 
 | ||||
| void furi_hal_rfid_pins_read() { | ||||
|     // ibutton low
 | ||||
|     furi_hal_ibutton_start(); | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_ibutton_pin_low(); | ||||
| 
 | ||||
|     // dont pull rfid antenna
 | ||||
|  | ||||
| @ -11,6 +11,8 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| extern uint32_t instructions_per_us; | ||||
| 
 | ||||
| /** Init DWT
 | ||||
|  */ | ||||
| void furi_hal_delay_init(void); | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include "furi_hal_gpio.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| 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_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(); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to high (float in iButton pin modes) | ||||
|  */ | ||||
| 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(); | ||||
| 
 | ||||
| #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 | ||||
| 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
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/common-api | ||||
| 
 | ||||
| @ -128,3 +118,8 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/flipper_format/*.c) | ||||
| # Micro-ECC
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/micro-ecc | ||||
| 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