 33e8bae78b
			
		
	
	
		33e8bae78b
		
			
		
	
	
	
	
		
			
			* AVR_ISP: fix NULL pointer dereference * SubGhz: double back with a blocked transmission in this region * SubGhz: fix speaker, when a transmission is blocked in this region * SubGhz: fix speaker * SubGhz: return region * AVR Flasher: cleanup code Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
		
			
				
	
	
		
			496 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			496 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "avr_isp.h"
 | |
| #include "../lib/driver/avr_isp_prog_cmd.h"
 | |
| #include "../lib/driver/avr_isp_spi_sw.h"
 | |
| 
 | |
| #include <furi.h>
 | |
| 
 | |
| #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320
 | |
| #define TAG "AvrIsp"
 | |
| 
 | |
| struct AvrIsp {
 | |
|     AvrIspSpiSw* spi;
 | |
|     bool pmode;
 | |
|     AvrIspCallback callback;
 | |
|     void* context;
 | |
| };
 | |
| 
 | |
| AvrIsp* avr_isp_alloc(void) {
 | |
|     AvrIsp* instance = malloc(sizeof(AvrIsp));
 | |
|     return instance;
 | |
| }
 | |
| 
 | |
| void avr_isp_free(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     if(instance->spi) avr_isp_end_pmode(instance);
 | |
|     free(instance);
 | |
| }
 | |
| 
 | |
| void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) {
 | |
|     furi_assert(instance);
 | |
|     furi_assert(context);
 | |
| 
 | |
|     instance->callback = callback;
 | |
|     instance->context = context;
 | |
| }
 | |
| 
 | |
| uint8_t avr_isp_spi_transaction(
 | |
|     AvrIsp* instance,
 | |
|     uint8_t cmd,
 | |
|     uint8_t addr_hi,
 | |
|     uint8_t addr_lo,
 | |
|     uint8_t data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     avr_isp_spi_sw_txrx(instance->spi, cmd);
 | |
|     avr_isp_spi_sw_txrx(instance->spi, addr_hi);
 | |
|     avr_isp_spi_sw_txrx(instance->spi, addr_lo);
 | |
|     return avr_isp_spi_sw_txrx(instance->spi, data);
 | |
| }
 | |
| 
 | |
| static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint8_t res = 0;
 | |
|     avr_isp_spi_sw_txrx(instance->spi, a);
 | |
|     avr_isp_spi_sw_txrx(instance->spi, b);
 | |
|     res = avr_isp_spi_sw_txrx(instance->spi, c);
 | |
|     avr_isp_spi_sw_txrx(instance->spi, d);
 | |
|     return res == 0x53;
 | |
| }
 | |
| 
 | |
| void avr_isp_end_pmode(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     if(instance->pmode) {
 | |
|         avr_isp_spi_sw_res_set(instance->spi, true);
 | |
|         // We're about to take the target out of reset
 | |
|         // so configure SPI pins as input
 | |
|         if(instance->spi) avr_isp_spi_sw_free(instance->spi);
 | |
|         instance->spi = NULL;
 | |
|     }
 | |
| 
 | |
|     instance->pmode = false;
 | |
| }
 | |
| 
 | |
| static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     // Reset target before driving PIN_SCK or PIN_MOSI
 | |
| 
 | |
|     // SPI.begin() will configure SS as output,
 | |
|     // so SPI master mode is selected.
 | |
|     // We have defined RESET as pin 10,
 | |
|     // which for many arduino's is not the SS pin.
 | |
|     // So we have to configure RESET as output here,
 | |
|     // (reset_target() first sets the correct level)
 | |
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi);
 | |
|     instance->spi = avr_isp_spi_sw_init(spi_speed);
 | |
| 
 | |
|     avr_isp_spi_sw_res_set(instance->spi, false);
 | |
|     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | |
| 
 | |
|     // Pulse RESET after PIN_SCK is low:
 | |
|     avr_isp_spi_sw_sck_set(instance->spi, false);
 | |
| 
 | |
|     // discharge PIN_SCK, value arbitrally chosen
 | |
|     furi_delay_ms(20);
 | |
|     avr_isp_spi_sw_res_set(instance->spi, true);
 | |
| 
 | |
|     // Pulse must be minimum 2 target CPU speed cycles
 | |
|     // so 100 usec is ok for CPU speeds above 20KHz
 | |
|     furi_delay_ms(1);
 | |
| 
 | |
|     avr_isp_spi_sw_res_set(instance->spi, false);
 | |
| 
 | |
|     // Send the enable programming command:
 | |
|     // datasheet: must be > 20 msec
 | |
|     furi_delay_ms(50);
 | |
|     if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) {
 | |
|         instance->pmode = true;
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     AvrIspSpiSwSpeed spi_speed[] = {
 | |
|         AvrIspSpiSwSpeed1Mhz,
 | |
|         AvrIspSpiSwSpeed400Khz,
 | |
|         AvrIspSpiSwSpeed250Khz,
 | |
|         AvrIspSpiSwSpeed125Khz,
 | |
|         AvrIspSpiSwSpeed60Khz,
 | |
|         AvrIspSpiSwSpeed40Khz,
 | |
|         AvrIspSpiSwSpeed20Khz,
 | |
|         AvrIspSpiSwSpeed10Khz,
 | |
|         AvrIspSpiSwSpeed5Khz,
 | |
|         AvrIspSpiSwSpeed1Khz,
 | |
|     };
 | |
|     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) {
 | |
|         if(avr_isp_start_pmode(instance, spi_speed[i])) {
 | |
|             AvrIspSignature sig = avr_isp_read_signature(instance);
 | |
|             AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
 | |
|             uint8_t y = 0;
 | |
|             while(y < 8) {
 | |
|                 if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) !=
 | |
|                    0)
 | |
|                     break;
 | |
|                 sig_examination = avr_isp_read_signature(instance);
 | |
|                 y++;
 | |
|             }
 | |
|             if(y == 8) {
 | |
|                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) {
 | |
|                     if(i < (COUNT_OF(spi_speed) - 1)) {
 | |
|                         avr_isp_end_pmode(instance);
 | |
|                         i++;
 | |
|                         return avr_isp_start_pmode(instance, spi_speed[i]);
 | |
|                     }
 | |
|                 }
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(instance->spi) {
 | |
|         avr_isp_spi_sw_free(instance->spi);
 | |
|         instance->spi = NULL;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr));
 | |
|     /* polling flash */
 | |
|     if(data == 0xFF) {
 | |
|         furi_delay_ms(5);
 | |
|     } else {
 | |
|         /* polling flash */
 | |
|         uint32_t starttime = furi_get_tick();
 | |
|         while((furi_get_tick() - starttime) < 30) {
 | |
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint16_t page = 0;
 | |
|     switch(page_size) {
 | |
|     case 32:
 | |
|         page = addr & 0xFFFFFFF0;
 | |
|         break;
 | |
|     case 64:
 | |
|         page = addr & 0xFFFFFFE0;
 | |
|         break;
 | |
|     case 128:
 | |
|         page = addr & 0xFFFFFFC0;
 | |
|         break;
 | |
|     case 256:
 | |
|         page = addr & 0xFFFFFF80;
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         page = addr;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     return page;
 | |
| }
 | |
| 
 | |
| static bool avr_isp_flash_write_pages(
 | |
|     AvrIsp* instance,
 | |
|     uint16_t addr,
 | |
|     uint16_t page_size,
 | |
|     uint8_t* data,
 | |
|     uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     size_t x = 0;
 | |
|     uint16_t page = avr_isp_current_page(instance, addr, page_size);
 | |
| 
 | |
|     while(x < data_size) {
 | |
|         if(page != avr_isp_current_page(instance, addr, page_size)) {
 | |
|             avr_isp_commit(instance, page, data[x - 1]);
 | |
|             page = avr_isp_current_page(instance, addr, page_size);
 | |
|         }
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++]));
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++]));
 | |
|         addr++;
 | |
|     }
 | |
|     avr_isp_commit(instance, page, data[x - 1]);
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool avr_isp_erase_chip(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance);
 | |
|     if(instance->pmode) {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP);
 | |
|         furi_delay_ms(100);
 | |
|         avr_isp_end_pmode(instance);
 | |
|         ret = true;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static bool
 | |
|     avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     for(uint16_t i = 0; i < data_size; i++) {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i]));
 | |
|         furi_delay_ms(10);
 | |
|         addr++;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool avr_isp_write_page(
 | |
|     AvrIsp* instance,
 | |
|     uint32_t mem_type,
 | |
|     uint32_t mem_size,
 | |
|     uint16_t addr,
 | |
|     uint16_t page_size,
 | |
|     uint8_t* data,
 | |
|     uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     switch(mem_type) {
 | |
|     case STK_SET_FLASH_TYPE:
 | |
|         if((addr + data_size / 2) <= mem_size) {
 | |
|             ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case STK_SET_EEPROM_TYPE:
 | |
|         if((addr + data_size) <= mem_size) {
 | |
|             ret = avr_isp_eeprom_write(instance, addr, data, data_size);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         furi_crash(TAG " Incorrect mem type.");
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static bool avr_isp_flash_read_page(
 | |
|     AvrIsp* instance,
 | |
|     uint16_t addr,
 | |
|     uint16_t page_size,
 | |
|     uint8_t* data,
 | |
|     uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     if(page_size > data_size) return false;
 | |
|     for(uint16_t i = 0; i < page_size; i += 2) {
 | |
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr));
 | |
|         data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr));
 | |
|         addr++;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static bool avr_isp_eeprom_read_page(
 | |
|     AvrIsp* instance,
 | |
|     uint16_t addr,
 | |
|     uint16_t page_size,
 | |
|     uint8_t* data,
 | |
|     uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     if(page_size > data_size) return false;
 | |
|     for(uint16_t i = 0; i < page_size; i++) {
 | |
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr));
 | |
|         addr++;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool avr_isp_read_page(
 | |
|     AvrIsp* instance,
 | |
|     uint32_t mem_type,
 | |
|     uint16_t addr,
 | |
|     uint16_t page_size,
 | |
|     uint8_t* data,
 | |
|     uint32_t data_size) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool res = false;
 | |
|     if(mem_type == STK_SET_FLASH_TYPE)
 | |
|         res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size);
 | |
|     if(mem_type == STK_SET_EEPROM_TYPE)
 | |
|         res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size);
 | |
| 
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| AvrIspSignature avr_isp_read_signature(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     AvrIspSignature signature;
 | |
|     signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR);
 | |
|     signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY);
 | |
|     signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER);
 | |
|     return signature;
 | |
| }
 | |
| 
 | |
| uint8_t avr_isp_read_lock_byte(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint8_t data = 0;
 | |
|     uint32_t starttime = furi_get_tick();
 | |
|     while((furi_get_tick() - starttime) < 300) {
 | |
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE);
 | |
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) {
 | |
|             break;
 | |
|         };
 | |
|         data = 0x00;
 | |
|     }
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     if(avr_isp_read_lock_byte(instance) == lock) {
 | |
|         ret = true;
 | |
|     } else {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock));
 | |
|         /* polling lock byte */
 | |
|         uint32_t starttime = furi_get_tick();
 | |
|         while((furi_get_tick() - starttime) < 30) {
 | |
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) {
 | |
|                 ret = true;
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| uint8_t avr_isp_read_fuse_low(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint8_t data = 0;
 | |
|     uint32_t starttime = furi_get_tick();
 | |
|     while((furi_get_tick() - starttime) < 300) {
 | |
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW);
 | |
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) {
 | |
|             break;
 | |
|         };
 | |
|         data = 0x00;
 | |
|     }
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     if(avr_isp_read_fuse_low(instance) == lfuse) {
 | |
|         ret = true;
 | |
|     } else {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse));
 | |
|         /* polling fuse */
 | |
|         uint32_t starttime = furi_get_tick();
 | |
|         while((furi_get_tick() - starttime) < 30) {
 | |
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) {
 | |
|                 ret = true;
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| uint8_t avr_isp_read_fuse_high(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint8_t data = 0;
 | |
|     uint32_t starttime = furi_get_tick();
 | |
|     while((furi_get_tick() - starttime) < 300) {
 | |
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH);
 | |
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) {
 | |
|             break;
 | |
|         };
 | |
|         data = 0x00;
 | |
|     }
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     if(avr_isp_read_fuse_high(instance) == hfuse) {
 | |
|         ret = true;
 | |
|     } else {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse));
 | |
|         /* polling fuse */
 | |
|         uint32_t starttime = furi_get_tick();
 | |
|         while((furi_get_tick() - starttime) < 30) {
 | |
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) {
 | |
|                 ret = true;
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     uint8_t data = 0;
 | |
|     uint32_t starttime = furi_get_tick();
 | |
|     while((furi_get_tick() - starttime) < 300) {
 | |
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED);
 | |
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) {
 | |
|             break;
 | |
|         };
 | |
|         data = 0x00;
 | |
|     }
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     bool ret = false;
 | |
|     if(avr_isp_read_fuse_extended(instance) == efuse) {
 | |
|         ret = true;
 | |
|     } else {
 | |
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse));
 | |
|         /* polling fuse */
 | |
|         uint32_t starttime = furi_get_tick();
 | |
|         while((furi_get_tick() - starttime) < 30) {
 | |
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) {
 | |
|                 ret = true;
 | |
|                 break;
 | |
|             };
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) {
 | |
|     furi_assert(instance);
 | |
| 
 | |
|     avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr));
 | |
|     furi_delay_ms(10);
 | |
| } |