Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						fd29e190b7
					
				
							
								
								
									
										19
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,7 +10,8 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f6 |   TARGETS: f6 f7 | ||||||
|  |   DEFAULT_TARGET: f6 | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   build: |   build: | ||||||
| @ -69,7 +70,7 @@ jobs: | |||||||
|           echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}" |           echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}" | ||||||
|           echo "::set-output name=suffix::${SUFFIX}" |           echo "::set-output name=suffix::${SUFFIX}" | ||||||
|           echo "::set-output name=short-hash::${SHA}" |           echo "::set-output name=short-hash::${SHA}" | ||||||
|           echo "::set-output name=latest-target::${TARGETS[${#TARGETS[@]}-1]}" |           echo "::set-output name=default-target::${DEFAULT_TARGET}" | ||||||
| 
 | 
 | ||||||
|       - name: 'Build bootloader in docker' |       - name: 'Build bootloader in docker' | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
| @ -169,12 +170,7 @@ jobs: | |||||||
|         run: | |         run: | | ||||||
|           test -d core2_firmware && rm -rf core2_firmware || true |           test -d core2_firmware && rm -rf core2_firmware || true | ||||||
|           mkdir core2_firmware |           mkdir core2_firmware | ||||||
|           cp \ |           ./scripts/assets.py copro lib/STM32CubeWB core2_firmware STM32WB5x | ||||||
|             lib/STM32CubeWB/package.xml \ |  | ||||||
|             lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw.bin \ |  | ||||||
|             lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw_for_fus_0_5_3.bin \ |  | ||||||
|             lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ |  | ||||||
|             core2_firmware |  | ||||||
|           tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware |           tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle scripts' |       - name: 'Bundle scripts' | ||||||
| @ -185,6 +181,7 @@ jobs: | |||||||
|       - name: 'Bundle resources' |       - name: 'Bundle resources' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} |         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||||
|         run: | |         run: | | ||||||
|  |           ./scripts/assets.py manifest assets/resources | ||||||
|           tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources |           tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
| @ -205,7 +202,7 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} |           args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} | ||||||
| 
 | 
 | ||||||
|       - name: Find Previous Comment |       - name: 'Find Previous Comment' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} |         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} | ||||||
|         uses: peter-evans/find-comment@v1 |         uses: peter-evans/find-comment@v1 | ||||||
|         id: fc |         id: fc | ||||||
| @ -214,12 +211,12 @@ jobs: | |||||||
|           comment-author: 'github-actions[bot]' |           comment-author: 'github-actions[bot]' | ||||||
|           body-includes: 'to flash the' |           body-includes: 'to flash the' | ||||||
| 
 | 
 | ||||||
|       - name: Create or update comment |       - name: 'Create or update comment' | ||||||
|         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}} |         if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}} | ||||||
|         uses: peter-evans/create-or-update-comment@v1 |         uses: peter-evans/create-or-update-comment@v1 | ||||||
|         with: |         with: | ||||||
|           comment-id: ${{ steps.fc.outputs.comment-id }} |           comment-id: ${{ steps.fc.outputs.comment-id }} | ||||||
|           issue-number: ${{ github.event.pull_request.number }} |           issue-number: ${{ github.event.pull_request.number }} | ||||||
|           body: | |           body: | | ||||||
|             [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.latest-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. |             [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. | ||||||
|           edit-mode: replace |           edit-mode: replace | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							| @ -10,7 +10,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f6 |   TARGETS: f6 f7 | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   lint_c_cpp: |   lint_c_cpp: | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								ReadMe.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								ReadMe.md
									
									
									
									
									
								
							| @ -14,7 +14,7 @@ Our goal is to create nice and clean code with good documentation, to make it a | |||||||
| 
 | 
 | ||||||
| Flipper Zero's firmware consists of three components: | Flipper Zero's firmware consists of three components: | ||||||
| 
 | 
 | ||||||
| - Core2 firmware set - proprietary componenets by ST: FUS + radio stack. | - Core2 firmware set - proprietary components by ST: FUS + radio stack. | ||||||
| - Core1 Bootloader - controls basic hardware initialization and loads firmware | - Core1 Bootloader - controls basic hardware initialization and loads firmware | ||||||
| - Core1 Firmware - HAL + OS + Drivers + Applications | - Core1 Firmware - HAL + OS + Drivers + Applications | ||||||
| 
 | 
 | ||||||
| @ -67,13 +67,13 @@ One liner: `./flash_core1_main.sh` | |||||||
|    ``` |    ``` | ||||||
| 3. Prepare the container: | 3. Prepare the container: | ||||||
|    ```sh |    ```sh | ||||||
|    docker compose up -d |    docker-compose up -d | ||||||
|    ``` |    ``` | ||||||
| 
 | 
 | ||||||
| ## Compile bootloader | ## Compile bootloader | ||||||
| 
 | 
 | ||||||
| ```sh | ```sh | ||||||
| docker compose exec dev make -j$(nproc) -C bootloader TARGET=f6 | docker-compose exec dev make -j$(nproc) -C bootloader TARGET=f6 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Bootloader compilation results: | Bootloader compilation results: | ||||||
| @ -85,7 +85,7 @@ Bootloader compilation results: | |||||||
| ## Compile firmware | ## Compile firmware | ||||||
| 
 | 
 | ||||||
| ```sh | ```sh | ||||||
| docker compose exec dev make -j$(nproc) -C firmware TARGET=f6 | docker-compose exec dev make -j$(nproc) -C firmware TARGET=f6 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Firmware compilation results: | Firmware compilation results: | ||||||
| @ -102,14 +102,14 @@ That's exactly how we generate our `full` builds. | |||||||
| 
 | 
 | ||||||
| 1. Concatenate HEX files: | 1. Concatenate HEX files: | ||||||
|    ```sh |    ```sh | ||||||
|    docker compose exec dev srec_cat \ |    docker-compose exec dev srec_cat \ | ||||||
|     bootloader/.obj/f6/bootloader.hex -Intel \ |     bootloader/.obj/f6/bootloader.hex -Intel \ | ||||||
|     firmware/.obj/f6/firmware.hex -Intel \ |     firmware/.obj/f6/firmware.hex -Intel \ | ||||||
|     -o firmware/.obj/f6/full.hex -Intel |     -o firmware/.obj/f6/full.hex -Intel | ||||||
|    ``` |    ``` | ||||||
| 2. Convert HEX to DFU: | 2. Convert HEX to DFU: | ||||||
|    ```sh |    ```sh | ||||||
|    docker compose exec dev hex2dfu \ |    docker-compose exec dev hex2dfu \ | ||||||
|     -i firmware/.obj/f6/full.hex \ |     -i firmware/.obj/f6/full.hex \ | ||||||
|     -o firmware/.obj/f6/full.dfu \ |     -o firmware/.obj/f6/full.dfu \ | ||||||
|     -l "Flipper Zero F6" |     -l "Flipper Zero F6" | ||||||
|  | |||||||
| @ -1,831 +1,69 @@ | |||||||
| #include "archive_i.h" | #include "archive_i.h" | ||||||
| 
 | 
 | ||||||
| static bool archive_get_filenames(ArchiveApp* archive); | bool archive_custom_event_callback(void* context, uint32_t event) { | ||||||
| 
 |  | ||||||
| static void update_offset(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             size_t array_size = files_array_size(model->files); |  | ||||||
|             uint16_t bounds = array_size > 3 ? 2 : array_size; |  | ||||||
| 
 |  | ||||||
|             if(array_size > 3 && model->idx >= array_size - 1) { |  | ||||||
|                 model->list_offset = model->idx - 3; |  | ||||||
|             } else if(model->list_offset < model->idx - bounds) { |  | ||||||
|                 model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0); |  | ||||||
|             } else if(model->list_offset > model->idx - bounds) { |  | ||||||
|                 model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_update_last_idx(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             archive->browser.last_idx[archive->browser.depth] = |  | ||||||
|                 CLAMP(model->idx, files_array_size(model->files) - 1, 0); |  | ||||||
|             model->idx = 0; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_switch_dir(ArchiveApp* archive, const char* path) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(path); |  | ||||||
|     string_set(archive->browser.path, path); |  | ||||||
|     archive_get_filenames(archive); |  | ||||||
|     update_offset(archive); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_switch_tab(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             model->tab_idx = archive->browser.tab_id; |  | ||||||
|             model->idx = 0; |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     archive->browser.depth = 0; |  | ||||||
|     archive_switch_dir(archive, tab_default_paths[archive->browser.tab_id]); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_leave_dir(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     char* last_char_ptr = strrchr(string_get_cstr(archive->browser.path), '/'); |  | ||||||
| 
 |  | ||||||
|     if(last_char_ptr) { |  | ||||||
|         size_t pos = last_char_ptr - string_get_cstr(archive->browser.path); |  | ||||||
|         string_left(archive->browser.path, pos); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     archive->browser.depth = CLAMP(archive->browser.depth - 1, MAX_DEPTH, 0); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             model->idx = archive->browser.last_idx[archive->browser.depth]; |  | ||||||
|             model->list_offset = |  | ||||||
|                 model->idx - |  | ||||||
|                 (files_array_size(model->files) > 3 ? 3 : files_array_size(model->files)); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     archive_switch_dir(archive, string_get_cstr(archive->browser.path)); |  | ||||||
|     update_offset(archive); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_enter_dir(ArchiveApp* archive, string_t name) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(name); |  | ||||||
| 
 |  | ||||||
|     archive_update_last_idx(archive); |  | ||||||
|     archive->browser.depth = CLAMP(archive->browser.depth + 1, MAX_DEPTH, 0); |  | ||||||
| 
 |  | ||||||
|     string_cat(archive->browser.path, "/"); |  | ||||||
|     string_cat(archive->browser.path, archive->browser.name); |  | ||||||
| 
 |  | ||||||
|     archive_switch_dir(archive, string_get_cstr(archive->browser.path)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const char* name) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(file_info); |  | ||||||
|     furi_assert(name); |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
|     const char* filter_ext_ptr = get_tab_ext(archive->browser.tab_id); |  | ||||||
| 
 |  | ||||||
|     if(strcmp(filter_ext_ptr, "*") == 0) { |  | ||||||
|         result = true; |  | ||||||
|     } else if(strstr(name, filter_ext_ptr) != NULL) { |  | ||||||
|         result = true; |  | ||||||
|     } else if(file_info->flags & FSF_DIRECTORY) { |  | ||||||
|         result = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { |  | ||||||
|     furi_assert(file); |  | ||||||
|     furi_assert(file_info); |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { |  | ||||||
|         if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { |  | ||||||
|             file->type = i; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(file_info->flags & FSF_DIRECTORY) { |  | ||||||
|         file->type = ArchiveFileTypeFolder; |  | ||||||
|     } else { |  | ||||||
|         file->type = ArchiveFileTypeUnknown; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_file_append(ArchiveApp* archive, const char* path, string_t string) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(path); |  | ||||||
|     furi_assert(string); |  | ||||||
| 
 |  | ||||||
|     FileWorker* file_worker = file_worker_alloc(false); |  | ||||||
| 
 |  | ||||||
|     if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { |  | ||||||
|         FURI_LOG_E("Archive", "Append open error"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { |  | ||||||
|         FURI_LOG_E("Archive", "Append write error"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     file_worker_close(file_worker); |  | ||||||
|     file_worker_free(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_view_add_item(ArchiveApp* archive, FileInfo* file_info, const char* name) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(file_info); |  | ||||||
|     furi_assert(name); |  | ||||||
| 
 |  | ||||||
|     ArchiveFile_t item; |  | ||||||
| 
 |  | ||||||
|     if(filter_by_extension(archive, file_info, name)) { |  | ||||||
|         ArchiveFile_t_init(&item); |  | ||||||
|         string_init_set_str(item.name, name); |  | ||||||
|         set_file_type(&item, file_info); |  | ||||||
| 
 |  | ||||||
|         with_view_model( |  | ||||||
|             archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|                 files_array_push_back(model->files, item); |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         ArchiveFile_t_clear(&item); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool archive_is_favorite(ArchiveApp* archive, ArchiveFile_t* selected) { |  | ||||||
|     furi_assert(selected); |  | ||||||
|     string_t path; |  | ||||||
|     string_t buffer; |  | ||||||
|     string_init(buffer); |  | ||||||
|     bool found = false; |  | ||||||
| 
 |  | ||||||
|     string_init_printf( |  | ||||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); |  | ||||||
| 
 |  | ||||||
|     bool load_result = |  | ||||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); |  | ||||||
| 
 |  | ||||||
|     if(load_result) { |  | ||||||
|         while(1) { |  | ||||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(!string_size(buffer)) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(!string_search(buffer, path)) { |  | ||||||
|                 found = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(buffer); |  | ||||||
|     string_clear(path); |  | ||||||
|     file_worker_close(archive->file_worker); |  | ||||||
| 
 |  | ||||||
|     return found; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool archive_favorites_read(ArchiveApp* archive) { |  | ||||||
|     string_t buffer; |  | ||||||
|     FileInfo file_info; |  | ||||||
|     string_init(buffer); |  | ||||||
| 
 |  | ||||||
|     bool load_result = |  | ||||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |  | ||||||
| 
 |  | ||||||
|     if(load_result) { |  | ||||||
|         while(1) { |  | ||||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(!string_size(buffer)) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             archive_view_add_item(archive, &file_info, string_get_cstr(buffer)); |  | ||||||
|             string_clean(buffer); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     string_clear(buffer); |  | ||||||
|     file_worker_close(archive->file_worker); |  | ||||||
| 
 |  | ||||||
|     return load_result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool |  | ||||||
|     archive_favorites_rename(ArchiveApp* archive, ArchiveFile_t* selected, const char* dst) { |  | ||||||
|     furi_assert(selected); |  | ||||||
|     string_t path; |  | ||||||
|     string_t buffer; |  | ||||||
|     string_t temp; |  | ||||||
| 
 |  | ||||||
|     string_init(buffer); |  | ||||||
|     string_init(temp); |  | ||||||
| 
 |  | ||||||
|     string_init_printf( |  | ||||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); |  | ||||||
|     bool load_result = |  | ||||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |  | ||||||
| 
 |  | ||||||
|     if(load_result) { |  | ||||||
|         while(1) { |  | ||||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(!string_size(buffer)) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             string_printf( |  | ||||||
|                 temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); |  | ||||||
|             archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); |  | ||||||
|             string_clean(temp); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(temp); |  | ||||||
|     string_clear(buffer); |  | ||||||
|     string_clear(path); |  | ||||||
| 
 |  | ||||||
|     file_worker_close(archive->file_worker); |  | ||||||
|     file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); |  | ||||||
|     file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); |  | ||||||
| 
 |  | ||||||
|     return load_result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool archive_favorites_delete(ArchiveApp* archive, ArchiveFile_t* selected) { |  | ||||||
|     furi_assert(selected); |  | ||||||
|     string_t path; |  | ||||||
|     string_t buffer; |  | ||||||
|     string_init(buffer); |  | ||||||
| 
 |  | ||||||
|     string_init_printf( |  | ||||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name)); |  | ||||||
| 
 |  | ||||||
|     bool load_result = |  | ||||||
|         file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |  | ||||||
|     if(load_result) { |  | ||||||
|         while(1) { |  | ||||||
|             if(!file_worker_read_until(archive->file_worker, buffer, '\n')) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(!string_size(buffer)) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(string_search(buffer, path)) { |  | ||||||
|                 string_t temp; |  | ||||||
|                 string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); |  | ||||||
|                 archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp); |  | ||||||
|                 string_clear(temp); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(buffer); |  | ||||||
|     string_clear(path); |  | ||||||
| 
 |  | ||||||
|     file_worker_close(archive->file_worker); |  | ||||||
|     file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH); |  | ||||||
|     file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); |  | ||||||
| 
 |  | ||||||
|     return load_result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool archive_read_dir(ArchiveApp* archive) { |  | ||||||
|     FileInfo file_info; |  | ||||||
|     File* directory = storage_file_alloc(archive->api); |  | ||||||
|     char name[MAX_NAME_LEN]; |  | ||||||
| 
 |  | ||||||
|     if(!storage_dir_open(directory, string_get_cstr(archive->browser.path))) { |  | ||||||
|         storage_dir_close(directory); |  | ||||||
|         storage_file_free(directory); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     while(1) { |  | ||||||
|         if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         uint16_t files_cnt; |  | ||||||
|         with_view_model( |  | ||||||
|             archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|                 files_cnt = files_array_size(model->files); |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         if(files_cnt > MAX_FILES) { |  | ||||||
|             break; |  | ||||||
|         } else if(storage_file_get_error(directory) == FSE_OK) { |  | ||||||
|             archive_view_add_item(archive, &file_info, name); |  | ||||||
|         } else { |  | ||||||
|             storage_dir_close(directory); |  | ||||||
|             storage_file_free(directory); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     storage_dir_close(directory); |  | ||||||
|     storage_file_free(directory); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool archive_get_filenames(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             files_array_clean(model->files); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     if(archive->browser.tab_id != ArchiveTabFavorites) { |  | ||||||
|         archive_read_dir(archive); |  | ||||||
|     } else { |  | ||||||
|         archive_favorites_read(archive); |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| static void archive_exit_callback(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     AppEvent event; |  | ||||||
|     event.type = EventTypeExit; |  | ||||||
|     furi_check(osMessageQueuePut(archive->event_queue, &event, 0, osWaitForever) == osOK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static uint32_t archive_previous_callback(void* context) { |  | ||||||
|     return ArchiveViewMain; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* file menu */ |  | ||||||
| static void archive_add_to_favorites(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     string_t buffer_src; |  | ||||||
| 
 |  | ||||||
|     string_init_printf( |  | ||||||
|         buffer_src, |  | ||||||
|         "%s/%s\r\n", |  | ||||||
|         string_get_cstr(archive->browser.path), |  | ||||||
|         string_get_cstr(archive->browser.name)); |  | ||||||
| 
 |  | ||||||
|     archive_file_append(archive, ARCHIVE_FAV_PATH, buffer_src); |  | ||||||
| 
 |  | ||||||
|     string_clear(buffer_src); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_text_input_callback(void* context) { |  | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |  | ||||||
|     ArchiveApp* archive = (ArchiveApp*)context; |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
| 
 |     return scene_manager_handle_custom_event(archive->scene_manager, event); | ||||||
|     string_t buffer_src; |  | ||||||
|     string_t buffer_dst; |  | ||||||
| 
 |  | ||||||
|     string_init_printf( |  | ||||||
|         buffer_src, |  | ||||||
|         "%s/%s", |  | ||||||
|         string_get_cstr(archive->browser.path), |  | ||||||
|         string_get_cstr(archive->browser.name)); |  | ||||||
|     string_init_printf( |  | ||||||
|         buffer_dst, |  | ||||||
|         "%s/%s", |  | ||||||
|         string_get_cstr(archive->browser.path), |  | ||||||
|         archive->browser.text_input_buffer); |  | ||||||
| 
 |  | ||||||
|     string_set(archive->browser.name, archive->browser.text_input_buffer); |  | ||||||
|     // append extension
 |  | ||||||
| 
 |  | ||||||
|     ArchiveFile_t* file; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             file = files_array_get( |  | ||||||
|                 model->files, CLAMP(model->idx, files_array_size(model->files) - 1, 0)); |  | ||||||
|             file->fav = archive_is_favorite(archive, file); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     string_cat(buffer_dst, known_ext[file->type]); |  | ||||||
|     storage_common_rename(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); |  | ||||||
| 
 |  | ||||||
|     if(file->fav) { |  | ||||||
|         archive_favorites_rename(archive, file, string_get_cstr(buffer_dst)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewMain); |  | ||||||
|     archive_get_filenames(archive); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             model->idx = 0; |  | ||||||
|             while(model->idx < files_array_size(model->files)) { |  | ||||||
|                 ArchiveFile_t* current = files_array_get(model->files, model->idx); |  | ||||||
|                 if(!string_search(current->name, archive->browser.text_input_buffer)) { |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 ++model->idx; |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     update_offset(archive); |  | ||||||
| 
 |  | ||||||
|     string_clear(buffer_src); |  | ||||||
|     string_clear(buffer_dst); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void archive_enter_text_input(ArchiveApp* archive) { | bool archive_back_event_callback(void* context) { | ||||||
|     furi_assert(archive); |  | ||||||
|     *archive->browser.text_input_buffer = '\0'; |  | ||||||
| 
 |  | ||||||
|     strlcpy( |  | ||||||
|         archive->browser.text_input_buffer, string_get_cstr(archive->browser.name), MAX_NAME_LEN); |  | ||||||
| 
 |  | ||||||
|     archive_trim_file_ext(archive->browser.text_input_buffer); |  | ||||||
| 
 |  | ||||||
|     text_input_set_header_text(archive->text_input, "Rename:"); |  | ||||||
| 
 |  | ||||||
|     text_input_set_result_callback( |  | ||||||
|         archive->text_input, |  | ||||||
|         archive_text_input_callback, |  | ||||||
|         archive, |  | ||||||
|         archive->browser.text_input_buffer, |  | ||||||
|         MAX_NAME_LEN, |  | ||||||
|         false); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_show_file_menu(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     archive->browser.menu = true; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             ArchiveFile_t* selected; |  | ||||||
|             selected = files_array_get(model->files, model->idx); |  | ||||||
|             model->menu = true; |  | ||||||
|             model->menu_idx = 0; |  | ||||||
|             selected->fav = is_known_app(selected->type) ? archive_is_favorite(archive, selected) : |  | ||||||
|                                                            false; |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_close_file_menu(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     archive->browser.menu = false; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             model->menu = false; |  | ||||||
|             model->menu_idx = 0; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_open_app(ArchiveApp* archive, const char* app_name, const char* args) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(app_name); |  | ||||||
| 
 |  | ||||||
|     loader_start(archive->loader, app_name, args); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(file); |  | ||||||
| 
 |  | ||||||
|     string_t path; |  | ||||||
|     string_init(path); |  | ||||||
| 
 |  | ||||||
|     string_printf( |  | ||||||
|         path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(file->name)); |  | ||||||
| 
 |  | ||||||
|     if(archive_is_favorite(archive, file)) { // remove from favorites
 |  | ||||||
|         archive_favorites_delete(archive, file); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     file_worker_remove(archive->file_worker, string_get_cstr(path)); |  | ||||||
| 
 |  | ||||||
|     string_clear(path); |  | ||||||
|     archive_get_filenames(archive); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     update_offset(archive); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void |  | ||||||
|     archive_run_in_app(ArchiveApp* archive, ArchiveFile_t* selected, bool full_path_provided) { |  | ||||||
|     string_t full_path; |  | ||||||
| 
 |  | ||||||
|     if(!full_path_provided) { |  | ||||||
|         string_init_printf( |  | ||||||
|             full_path, |  | ||||||
|             "%s/%s", |  | ||||||
|             string_get_cstr(archive->browser.path), |  | ||||||
|             string_get_cstr(selected->name)); |  | ||||||
|     } else { |  | ||||||
|         string_init_set(full_path, selected->name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     archive_open_app(archive, flipper_app_name[selected->type], string_get_cstr(full_path)); |  | ||||||
|     string_clear(full_path); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_file_menu_callback(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     ArchiveFile_t* selected; |  | ||||||
|     uint8_t idx = 0; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             selected = files_array_get(model->files, model->idx); |  | ||||||
|             idx = model->menu_idx; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     switch(idx) { |  | ||||||
|     case 0: |  | ||||||
|         if(is_known_app(selected->type)) { |  | ||||||
|             archive_run_in_app(archive, selected, false); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 1: |  | ||||||
|         if(is_known_app(selected->type)) { |  | ||||||
|             if(!archive_is_favorite(archive, selected)) { |  | ||||||
|                 string_set(archive->browser.name, selected->name); |  | ||||||
|                 archive_add_to_favorites(archive); |  | ||||||
|             } else { |  | ||||||
|                 // delete from favorites
 |  | ||||||
|                 archive_favorites_delete(archive, selected); |  | ||||||
|             } |  | ||||||
|             archive_close_file_menu(archive); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 2: |  | ||||||
|         // open rename view
 |  | ||||||
|         if(is_known_app(selected->type)) { |  | ||||||
|             archive_enter_text_input(archive); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     case 3: |  | ||||||
|         // confirmation?
 |  | ||||||
|         archive_delete_file(archive, selected); |  | ||||||
|         archive_close_file_menu(archive); |  | ||||||
| 
 |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     default: |  | ||||||
|         archive_close_file_menu(archive); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     selected = NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void menu_input_handler(ArchiveApp* archive, InputEvent* event) { |  | ||||||
|     furi_assert(archive); |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     if(event->type == InputTypeShort) { |  | ||||||
|         if(event->key == InputKeyUp || event->key == InputKeyDown) { |  | ||||||
|             with_view_model( |  | ||||||
|                 archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|                     if(event->key == InputKeyUp) { |  | ||||||
|                         model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; |  | ||||||
|                     } else if(event->key == InputKeyDown) { |  | ||||||
|                         model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; |  | ||||||
|                     } |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(event->key == InputKeyOk) { |  | ||||||
|             archive_file_menu_callback(archive); |  | ||||||
|         } else if(event->key == InputKeyBack) { |  | ||||||
|             archive_close_file_menu(archive); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* main controls */ |  | ||||||
| 
 |  | ||||||
| static bool archive_view_input(InputEvent* event, void* context) { |  | ||||||
|     furi_assert(event); |  | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|     ArchiveApp* archive = context; |     return scene_manager_handle_back_event(archive->scene_manager); | ||||||
|     bool in_menu = archive->browser.menu; |  | ||||||
| 
 |  | ||||||
|     if(in_menu) { |  | ||||||
|         menu_input_handler(archive, event); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(event->type == InputTypeShort) { |  | ||||||
|         if(event->key == InputKeyLeft) { |  | ||||||
|             if(archive->browser.tab_id > 0) { |  | ||||||
|                 archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0); |  | ||||||
|                 archive_switch_tab(archive); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } else if(event->key == InputKeyRight) { |  | ||||||
|             if(archive->browser.tab_id < ArchiveTabTotal - 1) { |  | ||||||
|                 archive->browser.tab_id = |  | ||||||
|                     CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0); |  | ||||||
|                 archive_switch_tab(archive); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } else if(event->key == InputKeyBack) { |  | ||||||
|             if(archive->browser.depth == 0) { |  | ||||||
|                 archive_exit_callback(archive); |  | ||||||
|             } else { |  | ||||||
|                 archive_leave_dir(archive); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     if(event->key == InputKeyUp || event->key == InputKeyDown) { |  | ||||||
|         with_view_model( |  | ||||||
|             archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|                 uint16_t num_elements = (uint16_t)files_array_size(model->files); |  | ||||||
|                 if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { |  | ||||||
|                     if(event->key == InputKeyUp) { |  | ||||||
|                         model->idx = ((model->idx - 1) + num_elements) % num_elements; |  | ||||||
|                     } else if(event->key == InputKeyDown) { |  | ||||||
|                         model->idx = (model->idx + 1) % num_elements; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
|         update_offset(archive); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(event->key == InputKeyOk) { |  | ||||||
|         ArchiveFile_t* selected; |  | ||||||
| 
 |  | ||||||
|         with_view_model( |  | ||||||
|             archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|                 selected = files_array_size(model->files) > 0 ? |  | ||||||
|                                files_array_get(model->files, model->idx) : |  | ||||||
|                                NULL; |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         if(selected) { |  | ||||||
|             string_set(archive->browser.name, selected->name); |  | ||||||
| 
 |  | ||||||
|             if(selected->type == ArchiveFileTypeFolder) { |  | ||||||
|                 if(event->type == InputTypeShort) { |  | ||||||
|                     archive_enter_dir(archive, archive->browser.name); |  | ||||||
|                 } else if(event->type == InputTypeLong) { |  | ||||||
|                     archive_show_file_menu(archive); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 if(event->type == InputTypeShort) { |  | ||||||
|                     if(archive->browser.tab_id == ArchiveTabFavorites) { |  | ||||||
|                         if(is_known_app(selected->type)) { |  | ||||||
|                             archive_run_in_app(archive, selected, true); |  | ||||||
|                         } |  | ||||||
|                     } else { |  | ||||||
|                         archive_show_file_menu(archive); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     update_offset(archive); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void archive_free(ArchiveApp* archive) { |  | ||||||
|     furi_assert(archive); |  | ||||||
| 
 |  | ||||||
|     file_worker_free(archive->file_worker); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewMain); |  | ||||||
|     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); |  | ||||||
|     view_dispatcher_free(archive->view_dispatcher); |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             files_array_clear(model->files); |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     view_free(archive->view_archive_main); |  | ||||||
| 
 |  | ||||||
|     string_clear(archive->browser.name); |  | ||||||
|     string_clear(archive->browser.path); |  | ||||||
| 
 |  | ||||||
|     text_input_free(archive->text_input); |  | ||||||
| 
 |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     archive->api = NULL; |  | ||||||
|     furi_record_close("gui"); |  | ||||||
|     archive->gui = NULL; |  | ||||||
|     furi_record_close("loader"); |  | ||||||
|     archive->loader = NULL; |  | ||||||
|     furi_thread_free(archive->app_thread); |  | ||||||
|     furi_check(osMessageQueueDelete(archive->event_queue) == osOK); |  | ||||||
| 
 |  | ||||||
|     free(archive); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ArchiveApp* archive_alloc() { | ArchiveApp* archive_alloc() { | ||||||
|     ArchiveApp* archive = furi_alloc(sizeof(ArchiveApp)); |     ArchiveApp* archive = furi_alloc(sizeof(ArchiveApp)); | ||||||
| 
 | 
 | ||||||
|     archive->event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL); |  | ||||||
|     archive->app_thread = furi_thread_alloc(); |  | ||||||
|     archive->gui = furi_record_open("gui"); |     archive->gui = furi_record_open("gui"); | ||||||
|     archive->loader = furi_record_open("loader"); |  | ||||||
|     archive->api = furi_record_open("storage"); |  | ||||||
|     archive->text_input = text_input_alloc(); |     archive->text_input = text_input_alloc(); | ||||||
|     archive->view_archive_main = view_alloc(); |  | ||||||
|     archive->file_worker = file_worker_alloc(true); |  | ||||||
| 
 | 
 | ||||||
|     furi_check(archive->event_queue); |  | ||||||
| 
 |  | ||||||
|     view_allocate_model( |  | ||||||
|         archive->view_archive_main, ViewModelTypeLocking, sizeof(ArchiveViewModel)); |  | ||||||
|     with_view_model( |  | ||||||
|         archive->view_archive_main, (ArchiveViewModel * model) { |  | ||||||
|             files_array_init(model->files); |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     view_set_context(archive->view_archive_main, archive); |  | ||||||
|     view_set_draw_callback(archive->view_archive_main, archive_view_render); |  | ||||||
|     view_set_input_callback(archive->view_archive_main, archive_view_input); |  | ||||||
|     view_set_previous_callback( |  | ||||||
|         text_input_get_view(archive->text_input), archive_previous_callback); |  | ||||||
| 
 |  | ||||||
|     // View Dispatcher
 |  | ||||||
|     archive->view_dispatcher = view_dispatcher_alloc(); |     archive->view_dispatcher = view_dispatcher_alloc(); | ||||||
|     view_dispatcher_add_view( |     archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); | ||||||
|         archive->view_dispatcher, ArchiveViewMain, archive->view_archive_main); | 
 | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_enable_queue(archive->view_dispatcher); | ||||||
|         archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); |  | ||||||
|     view_dispatcher_attach_to_gui( |     view_dispatcher_attach_to_gui( | ||||||
|         archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen); |         archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavorites); |     view_dispatcher_set_event_callback_context(archive->view_dispatcher, archive); | ||||||
|  |     view_dispatcher_set_custom_event_callback( | ||||||
|  |         archive->view_dispatcher, archive_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         archive->view_dispatcher, archive_back_event_callback); | ||||||
|  | 
 | ||||||
|  |     archive->main_view = main_view_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view)); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input)); | ||||||
| 
 | 
 | ||||||
|     return archive; |     return archive; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void archive_free(ArchiveApp* archive) { | ||||||
|  |     furi_assert(archive); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser); | ||||||
|  |     view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput); | ||||||
|  |     view_dispatcher_free(archive->view_dispatcher); | ||||||
|  |     scene_manager_free(archive->scene_manager); | ||||||
|  |     main_view_free(archive->main_view); | ||||||
|  | 
 | ||||||
|  |     text_input_free(archive->text_input); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     archive->gui = NULL; | ||||||
|  | 
 | ||||||
|  |     free(archive); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t archive_app(void* p) { | int32_t archive_app(void* p) { | ||||||
|     ArchiveApp* archive = archive_alloc(); |     ArchiveApp* archive = archive_alloc(); | ||||||
| 
 |     scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); | ||||||
|     // default tab
 |     view_dispatcher_run(archive->view_dispatcher); | ||||||
|     archive_switch_tab(archive); |  | ||||||
| 
 |  | ||||||
|     AppEvent event; |  | ||||||
|     while(1) { |  | ||||||
|         furi_check(osMessageQueueGet(archive->event_queue, &event, NULL, osWaitForever) == osOK); |  | ||||||
|         if(event.type == EventTypeExit) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     archive_free(archive); |     archive_free(archive); | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,44 +5,27 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <gui/gui_i.h> | #include <gui/gui_i.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/text_input.h> | #include <gui/modules/text_input.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <m-array.h> | #include <m-array.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include "archive_views.h" |  | ||||||
| #include "applications.h" | #include "applications.h" | ||||||
| #include "file-worker.h" | #include "file-worker.h" | ||||||
| 
 | 
 | ||||||
| #define MAX_DEPTH 32 | #include "views/archive_main_view.h" | ||||||
| #define MAX_FILES 100 //temp
 | #include "scenes/archive_scene.h" | ||||||
|  | 
 | ||||||
| #define MAX_FILE_SIZE 128 | #define MAX_FILE_SIZE 128 | ||||||
| #define ARCHIVE_FAV_PATH "/any/favorites.txt" |  | ||||||
| #define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     ArchiveViewMain, |     ArchiveViewBrowser, | ||||||
|     ArchiveViewTextInput, |     ArchiveViewTextInput, | ||||||
|     ArchiveViewTotal, |     ArchiveViewTotal, | ||||||
| } ArchiveViewEnum; | } ArchiveViewEnum; | ||||||
| 
 | 
 | ||||||
| static const char* flipper_app_name[] = { |  | ||||||
|     [ArchiveFileTypeIButton] = "iButton", |  | ||||||
|     [ArchiveFileTypeNFC] = "NFC", |  | ||||||
|     [ArchiveFileTypeSubGhz] = "Sub-GHz", |  | ||||||
|     [ArchiveFileTypeLFRFID] = "125 kHz RFID", |  | ||||||
|     [ArchiveFileTypeIrda] = "Infrared", |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const char* known_ext[] = { |  | ||||||
|     [ArchiveFileTypeIButton] = ".ibtn", |  | ||||||
|     [ArchiveFileTypeNFC] = ".nfc", |  | ||||||
|     [ArchiveFileTypeSubGhz] = ".sub", |  | ||||||
|     [ArchiveFileTypeLFRFID] = ".rfid", |  | ||||||
|     [ArchiveFileTypeIrda] = ".ir", |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const char* tab_default_paths[] = { | static const char* tab_default_paths[] = { | ||||||
|     [ArchiveTabFavorites] = "/any/favorites", |     [ArchiveTabFavorites] = "/any/favorites", | ||||||
|     [ArchiveTabIButton] = "/any/ibutton", |     [ArchiveTabIButton] = "/any/ibutton", | ||||||
| @ -53,23 +36,6 @@ static const char* tab_default_paths[] = { | |||||||
|     [ArchiveTabBrowser] = "/any", |     [ArchiveTabBrowser] = "/any", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline const char* get_tab_ext(ArchiveTabEnum tab) { |  | ||||||
|     switch(tab) { |  | ||||||
|     case ArchiveTabIButton: |  | ||||||
|         return known_ext[ArchiveFileTypeIButton]; |  | ||||||
|     case ArchiveTabNFC: |  | ||||||
|         return known_ext[ArchiveFileTypeNFC]; |  | ||||||
|     case ArchiveTabSubGhz: |  | ||||||
|         return known_ext[ArchiveFileTypeSubGhz]; |  | ||||||
|     case ArchiveTabLFRFID: |  | ||||||
|         return known_ext[ArchiveFileTypeLFRFID]; |  | ||||||
|     case ArchiveTabIrda: |  | ||||||
|         return known_ext[ArchiveFileTypeIrda]; |  | ||||||
|     default: |  | ||||||
|         return "*"; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline const char* get_default_path(ArchiveFileTypeEnum type) { | static inline const char* get_default_path(ArchiveFileTypeEnum type) { | ||||||
|     switch(type) { |     switch(type) { | ||||||
|     case ArchiveFileTypeIButton: |     case ArchiveFileTypeIButton: | ||||||
| @ -104,35 +70,11 @@ typedef struct { | |||||||
|     EventType type; |     EventType type; | ||||||
| } AppEvent; | } AppEvent; | ||||||
| 
 | 
 | ||||||
| typedef enum { |  | ||||||
|     FavoritesCheck, |  | ||||||
|     FavoritesRead, |  | ||||||
|     FavoritesDelete, |  | ||||||
|     FavoritesRename, |  | ||||||
| } FavActionsEnum; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     ArchiveTabEnum tab_id; |  | ||||||
|     string_t name; |  | ||||||
|     string_t path; |  | ||||||
|     char text_input_buffer[MAX_NAME_LEN]; |  | ||||||
| 
 |  | ||||||
|     uint8_t depth; |  | ||||||
|     uint16_t last_idx[MAX_DEPTH]; |  | ||||||
| 
 |  | ||||||
|     bool menu; |  | ||||||
| } ArchiveBrowser; |  | ||||||
| 
 |  | ||||||
| struct ArchiveApp { | struct ArchiveApp { | ||||||
|     osMessageQueueId_t event_queue; |  | ||||||
|     FuriThread* app_thread; |  | ||||||
|     Loader* loader; |  | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     View* view_archive_main; |     SceneManager* scene_manager; | ||||||
|  |     ArchiveMainView* main_view; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
| 
 |     char text_store[MAX_NAME_LEN]; | ||||||
|     Storage* api; |  | ||||||
|     FileWorker* file_worker; |  | ||||||
|     ArchiveBrowser browser; |  | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,168 +0,0 @@ | |||||||
| #include "archive_views.h" |  | ||||||
| 
 |  | ||||||
| static const char* ArchiveTabNames[] = { |  | ||||||
|     [ArchiveTabFavorites] = "Favorites", |  | ||||||
|     [ArchiveTabIButton] = "iButton", |  | ||||||
|     [ArchiveTabNFC] = "NFC", |  | ||||||
|     [ArchiveTabSubGhz] = "Sub-GHz", |  | ||||||
|     [ArchiveTabLFRFID] = "RFID LF", |  | ||||||
|     [ArchiveTabIrda] = "Infrared", |  | ||||||
|     [ArchiveTabBrowser] = "Browser"}; |  | ||||||
| 
 |  | ||||||
| static const Icon* ArchiveItemIcons[] = { |  | ||||||
|     [ArchiveFileTypeIButton] = &I_ibutt_10px, |  | ||||||
|     [ArchiveFileTypeNFC] = &I_Nfc_10px, |  | ||||||
|     [ArchiveFileTypeSubGhz] = &I_sub1_10px, |  | ||||||
|     [ArchiveFileTypeLFRFID] = &I_125_10px, |  | ||||||
|     [ArchiveFileTypeIrda] = &I_ir_10px, |  | ||||||
|     [ArchiveFileTypeFolder] = &I_dir_10px, |  | ||||||
|     [ArchiveFileTypeUnknown] = &I_unknown_10px, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) { |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_box(canvas, 71, 17, 57, 46); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); |  | ||||||
| 
 |  | ||||||
|     string_t menu[MENU_ITEMS]; |  | ||||||
| 
 |  | ||||||
|     string_init_set_str(menu[0], "Run in app"); |  | ||||||
|     string_init_set_str(menu[1], "Pin"); |  | ||||||
|     string_init_set_str(menu[2], "Rename"); |  | ||||||
|     string_init_set_str(menu[3], "Delete"); |  | ||||||
| 
 |  | ||||||
|     ArchiveFile_t* selected = files_array_get(model->files, model->idx); |  | ||||||
| 
 |  | ||||||
|     if(!is_known_app(selected->type)) { |  | ||||||
|         string_set_str(menu[0], "---"); |  | ||||||
|         string_set_str(menu[1], "---"); |  | ||||||
|         string_set_str(menu[2], "---"); |  | ||||||
|     } else if(model->tab_idx == 0 || selected->fav) { |  | ||||||
|         string_set_str(menu[1], "Unpin"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < MENU_ITEMS; i++) { |  | ||||||
|         canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); |  | ||||||
|         string_clear(menu[i]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void archive_trim_file_ext(char* name) { |  | ||||||
|     size_t str_len = strlen(name); |  | ||||||
|     char* end = name + str_len; |  | ||||||
|     while(end > name && *end != '.' && *end != '\\' && *end != '/') { |  | ||||||
|         --end; |  | ||||||
|     } |  | ||||||
|     if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) { |  | ||||||
|         *end = '\0'; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); |  | ||||||
|     canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); |  | ||||||
|     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); |  | ||||||
| 
 |  | ||||||
|     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); |  | ||||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); |  | ||||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void draw_list(Canvas* canvas, ArchiveViewModel* model) { |  | ||||||
|     furi_assert(model); |  | ||||||
| 
 |  | ||||||
|     size_t array_size = files_array_size(model->files); |  | ||||||
|     bool scrollbar = array_size > 4; |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { |  | ||||||
|         string_t str_buff; |  | ||||||
|         char cstr_buff[MAX_NAME_LEN]; |  | ||||||
| 
 |  | ||||||
|         size_t idx = CLAMP(i + model->list_offset, array_size, 0); |  | ||||||
|         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); |  | ||||||
| 
 |  | ||||||
|         string_init_set(str_buff, file->name); |  | ||||||
|         string_right(str_buff, string_search_rchar(str_buff, '/') + 1); |  | ||||||
|         strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); |  | ||||||
| 
 |  | ||||||
|         if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); |  | ||||||
| 
 |  | ||||||
|         string_clean(str_buff); |  | ||||||
|         string_set_str(str_buff, cstr_buff); |  | ||||||
| 
 |  | ||||||
|         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); |  | ||||||
| 
 |  | ||||||
|         if(model->idx == idx) { |  | ||||||
|             archive_draw_frame(canvas, i, scrollbar); |  | ||||||
|         } else { |  | ||||||
|             canvas_set_color(canvas, ColorBlack); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); |  | ||||||
|         canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); |  | ||||||
|         string_clear(str_buff); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(scrollbar) { |  | ||||||
|         elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(model->menu) { |  | ||||||
|         render_item_menu(canvas, model); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) { |  | ||||||
|     furi_assert(model); |  | ||||||
| 
 |  | ||||||
|     const char* tab_name = ArchiveTabNames[model->tab_idx]; |  | ||||||
| 
 |  | ||||||
|     canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_box(canvas, 0, 0, 50, 13); |  | ||||||
|     canvas_draw_box(canvas, 107, 0, 20, 13); |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_draw_frame(canvas, 1, 0, 50, 12); |  | ||||||
|     canvas_draw_line(canvas, 0, 1, 0, 11); |  | ||||||
|     canvas_draw_line(canvas, 1, 12, 49, 12); |  | ||||||
|     canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); |  | ||||||
| 
 |  | ||||||
|     canvas_draw_frame(canvas, 108, 0, 20, 12); |  | ||||||
|     canvas_draw_line(canvas, 107, 1, 107, 11); |  | ||||||
|     canvas_draw_line(canvas, 108, 12, 126, 12); |  | ||||||
| 
 |  | ||||||
|     if(model->tab_idx > 0) { |  | ||||||
|         canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); |  | ||||||
|     } |  | ||||||
|     if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { |  | ||||||
|         canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_dot(canvas, 50, 0); |  | ||||||
|     canvas_draw_dot(canvas, 127, 0); |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void archive_view_render(Canvas* canvas, void* model) { |  | ||||||
|     ArchiveViewModel* m = model; |  | ||||||
| 
 |  | ||||||
|     archive_render_status_bar(canvas, model); |  | ||||||
| 
 |  | ||||||
|     if(files_array_size(m->files) > 0) { |  | ||||||
|         draw_list(canvas, m); |  | ||||||
|     } else { |  | ||||||
|         canvas_draw_str_aligned( |  | ||||||
|             canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										171
									
								
								applications/archive/helpers/archive_favorites.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								applications/archive/helpers/archive_favorites.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | |||||||
|  | #include "archive_favorites.h" | ||||||
|  | #include "archive_files.h" | ||||||
|  | #include "../views/archive_main_view.h" | ||||||
|  | 
 | ||||||
|  | bool archive_favorites_read(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ArchiveMainView* archive_view = context; | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
|  | 
 | ||||||
|  |     string_t buffer; | ||||||
|  |     FileInfo file_info; | ||||||
|  |     string_init(buffer); | ||||||
|  | 
 | ||||||
|  |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); | ||||||
|  | 
 | ||||||
|  |     if(result) { | ||||||
|  |         while(1) { | ||||||
|  |             if(!file_worker_read_until(file_worker, buffer, '\n')) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!string_size(buffer)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer)); | ||||||
|  |             string_clean(buffer); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     string_clear(buffer); | ||||||
|  |     file_worker_close(file_worker); | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_favorites_delete(const char* file_path, const char* name) { | ||||||
|  |     furi_assert(file_path); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
|  | 
 | ||||||
|  |     string_t path; | ||||||
|  |     string_t buffer; | ||||||
|  |     string_init(buffer); | ||||||
|  | 
 | ||||||
|  |     string_init_printf(path, "%s/%s", file_path, name); | ||||||
|  | 
 | ||||||
|  |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
|  |     if(result) { | ||||||
|  |         while(1) { | ||||||
|  |             if(!file_worker_read_until(file_worker, buffer, '\n')) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!string_size(buffer)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(string_search(buffer, path)) { | ||||||
|  |                 string_t temp; | ||||||
|  |                 string_init_printf(temp, "%s\r\n", string_get_cstr(buffer)); | ||||||
|  |                 archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); | ||||||
|  |                 string_clear(temp); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(buffer); | ||||||
|  |     string_clear(path); | ||||||
|  | 
 | ||||||
|  |     file_worker_close(file_worker); | ||||||
|  |     file_worker_remove(file_worker, ARCHIVE_FAV_PATH); | ||||||
|  |     file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); | ||||||
|  | 
 | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_is_favorite(const char* file_path, const char* name) { | ||||||
|  |     furi_assert(file_path); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
|  | 
 | ||||||
|  |     string_t path; | ||||||
|  |     string_t buffer; | ||||||
|  |     string_init(buffer); | ||||||
|  |     bool found = false; | ||||||
|  | 
 | ||||||
|  |     string_init_printf(path, "%s/%s", file_path, name); | ||||||
|  |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS); | ||||||
|  | 
 | ||||||
|  |     if(result) { | ||||||
|  |         while(1) { | ||||||
|  |             if(!file_worker_read_until(file_worker, buffer, '\n')) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!string_size(buffer)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!string_search(buffer, path)) { | ||||||
|  |                 found = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(buffer); | ||||||
|  |     string_clear(path); | ||||||
|  |     file_worker_close(file_worker); | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  | 
 | ||||||
|  |     return found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) { | ||||||
|  |     furi_assert(file_path); | ||||||
|  |     furi_assert(src); | ||||||
|  |     furi_assert(dst); | ||||||
|  | 
 | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
|  | 
 | ||||||
|  |     string_t path; | ||||||
|  |     string_t buffer; | ||||||
|  |     string_t temp; | ||||||
|  | 
 | ||||||
|  |     string_init(buffer); | ||||||
|  |     string_init(temp); | ||||||
|  |     string_init(path); | ||||||
|  | 
 | ||||||
|  |     string_printf(path, "%s/%s", file_path, src); | ||||||
|  |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
|  | 
 | ||||||
|  |     if(result) { | ||||||
|  |         while(1) { | ||||||
|  |             if(!file_worker_read_until(file_worker, buffer, '\n')) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(!string_size(buffer)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             string_printf( | ||||||
|  |                 temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst); | ||||||
|  |             archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp); | ||||||
|  |             string_clean(temp); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(temp); | ||||||
|  |     string_clear(buffer); | ||||||
|  |     string_clear(path); | ||||||
|  | 
 | ||||||
|  |     file_worker_close(file_worker); | ||||||
|  |     file_worker_remove(file_worker, ARCHIVE_FAV_PATH); | ||||||
|  |     file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); | ||||||
|  | 
 | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_add_to_favorites(const char* file_path, const char* name) { | ||||||
|  |     furi_assert(file_path); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     string_t buffer_src; | ||||||
|  | 
 | ||||||
|  |     string_init_printf(buffer_src, "%s/%s\r\n", file_path, name); | ||||||
|  |     archive_file_append(ARCHIVE_FAV_PATH, buffer_src); | ||||||
|  |     string_clear(buffer_src); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								applications/archive/helpers/archive_favorites.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/archive/helpers/archive_favorites.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "file-worker.h" | ||||||
|  | 
 | ||||||
|  | #define ARCHIVE_FAV_PATH "/any/favorites.txt" | ||||||
|  | #define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp" | ||||||
|  | 
 | ||||||
|  | bool archive_favorites_read(void* context); | ||||||
|  | bool archive_favorites_delete(const char* file_path, const char* name); | ||||||
|  | bool archive_is_favorite(const char* file_path, const char* name); | ||||||
|  | bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); | ||||||
|  | void archive_add_to_favorites(const char* file_path, const char* name); | ||||||
							
								
								
									
										143
									
								
								applications/archive/helpers/archive_files.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								applications/archive/helpers/archive_files.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | #include "archive_files.h" | ||||||
|  | #include "archive_favorites.h" | ||||||
|  | #include "../archive_i.h" | ||||||
|  | 
 | ||||||
|  | bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { | ||||||
|  |     furi_assert(file_info); | ||||||
|  |     furi_assert(tab_ext); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     if(strcmp(tab_ext, "*") == 0) { | ||||||
|  |         result = true; | ||||||
|  |     } else if(strstr(name, tab_ext) != NULL) { | ||||||
|  |         result = true; | ||||||
|  |     } else if(file_info->flags & FSF_DIRECTORY) { | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_trim_file_ext(char* name) { | ||||||
|  |     size_t str_len = strlen(name); | ||||||
|  |     char* end = name + str_len; | ||||||
|  |     while(end > name && *end != '.' && *end != '\\' && *end != '/') { | ||||||
|  |         --end; | ||||||
|  |     } | ||||||
|  |     if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) { | ||||||
|  |         *end = '\0'; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { | ||||||
|  |     furi_assert(file); | ||||||
|  |     furi_assert(file_info); | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) { | ||||||
|  |         if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { | ||||||
|  |             file->type = i; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(file_info->flags & FSF_DIRECTORY) { | ||||||
|  |         file->type = ArchiveFileTypeFolder; | ||||||
|  |     } else { | ||||||
|  |         file->type = ArchiveFileTypeUnknown; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ArchiveMainView* main_view = context; | ||||||
|  |     archive_file_array_clean(main_view); | ||||||
|  | 
 | ||||||
|  |     if(tab_id != ArchiveTabFavorites) { | ||||||
|  |         archive_read_dir(main_view, path); | ||||||
|  |     } else { | ||||||
|  |         archive_favorites_read(main_view); | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_read_dir(void* context, const char* path) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ArchiveMainView* main_view = context; | ||||||
|  |     FileInfo file_info; | ||||||
|  |     Storage* fs_api = furi_record_open("storage"); | ||||||
|  |     File* directory = storage_file_alloc(fs_api); | ||||||
|  |     char name[MAX_NAME_LEN]; | ||||||
|  | 
 | ||||||
|  |     if(!storage_dir_open(directory, path)) { | ||||||
|  |         storage_dir_close(directory); | ||||||
|  |         storage_file_free(directory); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint16_t files_cnt = archive_file_array_size(main_view); | ||||||
|  | 
 | ||||||
|  |         if(files_cnt > MAX_FILES) { | ||||||
|  |             break; | ||||||
|  |         } else if(storage_file_get_error(directory) == FSE_OK) { | ||||||
|  |             archive_view_add_item(main_view, &file_info, name); | ||||||
|  |         } else { | ||||||
|  |             storage_dir_close(directory); | ||||||
|  |             storage_file_free(directory); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     storage_dir_close(directory); | ||||||
|  |     storage_file_free(directory); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_file_append(const char* path, string_t string) { | ||||||
|  |     furi_assert(path); | ||||||
|  |     furi_assert(string); | ||||||
|  | 
 | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(false); | ||||||
|  | 
 | ||||||
|  |     if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) { | ||||||
|  |         FURI_LOG_E("Archive", "Append open error"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { | ||||||
|  |         FURI_LOG_E("Archive", "Append write error"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     file_worker_close(file_worker); | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_delete_file(void* context, string_t path, string_t name) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     furi_assert(path); | ||||||
|  |     furi_assert(name); | ||||||
|  |     ArchiveMainView* main_view = context; | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(false); | ||||||
|  | 
 | ||||||
|  |     string_t full_path; | ||||||
|  |     string_init(full_path); | ||||||
|  |     string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name)); | ||||||
|  |     file_worker_remove(file_worker, string_get_cstr(full_path)); | ||||||
|  |     file_worker_free(file_worker); | ||||||
|  |     string_clear(full_path); | ||||||
|  | 
 | ||||||
|  |     if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) { | ||||||
|  |         archive_favorites_delete(string_get_cstr(path), string_get_cstr(name)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     archive_file_array_remove_selected(main_view); | ||||||
|  | } | ||||||
| @ -1,15 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include "file-worker.h" | ||||||
| 
 | 
 | ||||||
| #include <gui/gui_i.h> | #define MAX_FILES 100 //temp
 | ||||||
| #include <gui/canvas.h> |  | ||||||
| #include <gui/elements.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| 
 |  | ||||||
| #define MAX_LEN_PX 100 |  | ||||||
| #define MAX_NAME_LEN 255 |  | ||||||
| #define FRAME_HEIGHT 12 |  | ||||||
| #define MENU_ITEMS 4 |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     ArchiveFileTypeIButton, |     ArchiveFileTypeIButton, | ||||||
| @ -22,17 +14,6 @@ typedef enum { | |||||||
|     AppIdTotal, |     AppIdTotal, | ||||||
| } ArchiveFileTypeEnum; | } ArchiveFileTypeEnum; | ||||||
| 
 | 
 | ||||||
| typedef enum { |  | ||||||
|     ArchiveTabFavorites, |  | ||||||
|     ArchiveTabLFRFID, |  | ||||||
|     ArchiveTabSubGhz, |  | ||||||
|     ArchiveTabNFC, |  | ||||||
|     ArchiveTabIButton, |  | ||||||
|     ArchiveTabIrda, |  | ||||||
|     ArchiveTabBrowser, |  | ||||||
|     ArchiveTabTotal, |  | ||||||
| } ArchiveTabEnum; |  | ||||||
| 
 |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     string_t name; |     string_t name; | ||||||
|     ArchiveFileTypeEnum type; |     ArchiveFileTypeEnum type; | ||||||
| @ -66,18 +47,10 @@ ARRAY_DEF( | |||||||
|      INIT_SET(API_6(ArchiveFile_t_init_set)), |      INIT_SET(API_6(ArchiveFile_t_init_set)), | ||||||
|      CLEAR(API_2(ArchiveFile_t_clear)))) |      CLEAR(API_2(ArchiveFile_t_clear)))) | ||||||
| 
 | 
 | ||||||
| typedef struct { | bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); | ||||||
|     uint8_t tab_idx; | void set_file_type(ArchiveFile_t* file, FileInfo* file_info); | ||||||
|     uint8_t menu_idx; |  | ||||||
|     uint16_t idx; |  | ||||||
|     uint16_t list_offset; |  | ||||||
|     files_array_t files; |  | ||||||
|     bool menu; |  | ||||||
| } ArchiveViewModel; |  | ||||||
| 
 |  | ||||||
| void archive_view_render(Canvas* canvas, void* model); |  | ||||||
| void archive_trim_file_ext(char* name); | void archive_trim_file_ext(char* name); | ||||||
| 
 | bool archive_get_filenames(void* context, uint8_t tab_id, const char* path); | ||||||
| static inline bool is_known_app(ArchiveFileTypeEnum type) { | bool archive_read_dir(void* context, const char* path); | ||||||
|     return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); | void archive_file_append(const char* path, string_t string); | ||||||
| } | void archive_delete_file(void* context, string_t path, string_t name); | ||||||
							
								
								
									
										30
									
								
								applications/archive/scenes/archive_scene.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/archive/scenes/archive_scene.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | #include "archive_scene.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const archive_on_enter_handlers[])(void*) = { | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const archive_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const archive_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers archive_scene_handlers = { | ||||||
|  |     .on_enter_handlers = archive_on_enter_handlers, | ||||||
|  |     .on_event_handlers = archive_on_event_handlers, | ||||||
|  |     .on_exit_handlers = archive_on_exit_handlers, | ||||||
|  |     .scene_num = ArchiveAppSceneNum, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/archive/scenes/archive_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/archive/scenes/archive_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) ArchiveAppScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  |     ArchiveAppSceneNum, | ||||||
|  | } ArchiveAppScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers archive_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "archive_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
							
								
								
									
										42
									
								
								applications/archive/scenes/archive_scene_browser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								applications/archive/scenes/archive_scene_browser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | #include "../archive_i.h" | ||||||
|  | #include "../views/archive_main_view.h" | ||||||
|  | 
 | ||||||
|  | void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     view_dispatcher_send_custom_event(archive->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void archive_scene_browser_on_enter(void* context) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     ArchiveMainView* main_view = archive->main_view; | ||||||
|  | 
 | ||||||
|  |     archive_browser_set_callback(main_view, archive_scene_browser_callback, archive); | ||||||
|  |     archive_browser_update(main_view); | ||||||
|  |     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     bool consumed; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case ArchiveBrowserEventRename: | ||||||
|  |             scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventExit: | ||||||
|  |             view_dispatcher_stop(archive->view_dispatcher); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void archive_scene_browser_on_exit(void* context) { | ||||||
|  |     // ArchiveApp* archive = (ArchiveApp*)context;
 | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								applications/archive/scenes/archive_scene_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								applications/archive/scenes/archive_scene_config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | ADD_SCENE(archive, browser, Browser) | ||||||
|  | ADD_SCENE(archive, rename, Rename) | ||||||
							
								
								
									
										80
									
								
								applications/archive/scenes/archive_scene_rename.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								applications/archive/scenes/archive_scene_rename.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | #include "../archive_i.h" | ||||||
|  | #include "../helpers/archive_favorites.h" | ||||||
|  | #include "../helpers/archive_files.h" | ||||||
|  | 
 | ||||||
|  | #define SCENE_RENAME_CUSTOM_EVENT (0UL) | ||||||
|  | 
 | ||||||
|  | void archive_scene_rename_text_input_callback(void* context) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void archive_scene_rename_on_enter(void* context) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  | 
 | ||||||
|  |     TextInput* text_input = archive->text_input; | ||||||
|  |     ArchiveFile_t* current = archive_get_current_file(archive->main_view); | ||||||
|  |     strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN); | ||||||
|  | 
 | ||||||
|  |     archive_trim_file_ext(archive->text_store); | ||||||
|  | 
 | ||||||
|  |     text_input_set_header_text(text_input, "Rename:"); | ||||||
|  | 
 | ||||||
|  |     text_input_set_result_callback( | ||||||
|  |         text_input, | ||||||
|  |         archive_scene_rename_text_input_callback, | ||||||
|  |         archive, | ||||||
|  |         archive->text_store, | ||||||
|  |         MAX_NAME_LEN, | ||||||
|  |         false); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SCENE_RENAME_CUSTOM_EVENT) { | ||||||
|  |             Storage* fs_api = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |             string_t buffer_src; | ||||||
|  |             string_t buffer_dst; | ||||||
|  | 
 | ||||||
|  |             const char* path = archive_get_path(archive->main_view); | ||||||
|  |             const char* name = archive_get_name(archive->main_view); | ||||||
|  | 
 | ||||||
|  |             string_init_printf(buffer_src, "%s/%s", path, name); | ||||||
|  |             string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); | ||||||
|  | 
 | ||||||
|  |             archive_set_name(archive->main_view, archive->text_store); | ||||||
|  | 
 | ||||||
|  |             // append extension
 | ||||||
|  |             ArchiveFile_t* file = archive_get_current_file(archive->main_view); | ||||||
|  | 
 | ||||||
|  |             string_cat(buffer_dst, known_ext[file->type]); | ||||||
|  |             storage_common_rename( | ||||||
|  |                 fs_api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); | ||||||
|  |             furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |             if(file->fav) { | ||||||
|  |                 archive_favorites_rename(path, name, string_get_cstr(buffer_dst)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string_clear(buffer_src); | ||||||
|  |             string_clear(buffer_dst); | ||||||
|  | 
 | ||||||
|  |             scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void archive_scene_rename_on_exit(void* context) { | ||||||
|  |     ArchiveApp* archive = (ArchiveApp*)context; | ||||||
|  |     // Clear view
 | ||||||
|  |     text_input_set_header_text(archive->text_input, NULL); | ||||||
|  |     text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false); | ||||||
|  | } | ||||||
							
								
								
									
										641
									
								
								applications/archive/views/archive_main_view.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										641
									
								
								applications/archive/views/archive_main_view.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,641 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include "../archive_i.h" | ||||||
|  | #include "archive_main_view.h" | ||||||
|  | 
 | ||||||
|  | static const char* flipper_app_name[] = { | ||||||
|  |     [ArchiveFileTypeIButton] = "iButton", | ||||||
|  |     [ArchiveFileTypeNFC] = "NFC", | ||||||
|  |     [ArchiveFileTypeSubGhz] = "Sub-GHz", | ||||||
|  |     [ArchiveFileTypeLFRFID] = "125 kHz RFID", | ||||||
|  |     [ArchiveFileTypeIrda] = "Infrared", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const char* ArchiveTabNames[] = { | ||||||
|  |     [ArchiveTabFavorites] = "Favorites", | ||||||
|  |     [ArchiveTabIButton] = "iButton", | ||||||
|  |     [ArchiveTabNFC] = "NFC", | ||||||
|  |     [ArchiveTabSubGhz] = "Sub-GHz", | ||||||
|  |     [ArchiveTabLFRFID] = "RFID LF", | ||||||
|  |     [ArchiveTabIrda] = "Infrared", | ||||||
|  |     [ArchiveTabBrowser] = "Browser"}; | ||||||
|  | 
 | ||||||
|  | static const Icon* ArchiveItemIcons[] = { | ||||||
|  |     [ArchiveFileTypeIButton] = &I_ibutt_10px, | ||||||
|  |     [ArchiveFileTypeNFC] = &I_Nfc_10px, | ||||||
|  |     [ArchiveFileTypeSubGhz] = &I_sub1_10px, | ||||||
|  |     [ArchiveFileTypeLFRFID] = &I_125_10px, | ||||||
|  |     [ArchiveFileTypeIrda] = &I_ir_10px, | ||||||
|  |     [ArchiveFileTypeFolder] = &I_dir_10px, | ||||||
|  |     [ArchiveFileTypeUnknown] = &I_unknown_10px, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void archive_browser_set_callback( | ||||||
|  |     ArchiveMainView* main_view, | ||||||
|  |     ArchiveMainViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     furi_assert(callback); | ||||||
|  |     main_view->callback = callback; | ||||||
|  |     main_view->context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void update_offset(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             size_t array_size = files_array_size(model->files); | ||||||
|  |             uint16_t bounds = array_size > 3 ? 2 : array_size; | ||||||
|  | 
 | ||||||
|  |             if(array_size > 3 && model->idx >= array_size - 1) { | ||||||
|  |                 model->list_offset = model->idx - 3; | ||||||
|  |             } else if(model->list_offset < model->idx - bounds) { | ||||||
|  |                 model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0); | ||||||
|  |             } else if(model->list_offset > model->idx - bounds) { | ||||||
|  |                 model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t archive_file_array_size(ArchiveMainView* main_view) { | ||||||
|  |     uint16_t size = 0; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             size = files_array_size(model->files); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     return size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_file_array_remove_selected(ArchiveMainView* main_view) { | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             files_array_remove_v(model->files, model->idx, model->idx + 1); | ||||||
|  |             model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     update_offset(main_view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_file_array_clean(ArchiveMainView* main_view) { | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             files_array_clean(model->files); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) { | ||||||
|  |     ArchiveFile_t* selected; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             selected = files_array_size(model->files) > 0 ? | ||||||
|  |                            files_array_get(model->files, model->idx) : | ||||||
|  |                            NULL; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     return selected; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) { | ||||||
|  |     ArchiveTabEnum tab_id; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             tab_id = model->tab_idx; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     return tab_id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) { | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             model->tab_idx = tab; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t archive_get_depth(ArchiveMainView* main_view) { | ||||||
|  |     uint8_t depth; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             depth = model->depth; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return depth; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* archive_get_path(ArchiveMainView* main_view) { | ||||||
|  |     return string_get_cstr(main_view->path); | ||||||
|  | } | ||||||
|  | const char* archive_get_name(ArchiveMainView* main_view) { | ||||||
|  |     ArchiveFile_t* selected = archive_get_current_file(main_view); | ||||||
|  |     return string_get_cstr(selected->name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_set_name(ArchiveMainView* main_view, const char* name) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     string_set(main_view->name, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_browser_update(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             uint16_t idx = 0; | ||||||
|  |             while(idx < files_array_size(model->files)) { | ||||||
|  |                 ArchiveFile_t* current = files_array_get(model->files, idx); | ||||||
|  |                 if(!string_search(current->name, string_get_cstr(main_view->name))) { | ||||||
|  |                     model->idx = idx; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 ++idx; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     update_offset(main_view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     furi_assert(file_info); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     ArchiveFile_t item; | ||||||
|  | 
 | ||||||
|  |     if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) { | ||||||
|  |         ArchiveFile_t_init(&item); | ||||||
|  |         string_init_set_str(item.name, name); | ||||||
|  |         set_file_type(&item, file_info); | ||||||
|  | 
 | ||||||
|  |         with_view_model( | ||||||
|  |             main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |                 files_array_push_back(model->files, item); | ||||||
|  |                 return true; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |         ArchiveFile_t_clear(&item); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) { | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_box(canvas, 71, 17, 57, 46); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); | ||||||
|  | 
 | ||||||
|  |     string_t menu[MENU_ITEMS]; | ||||||
|  | 
 | ||||||
|  |     string_init_set_str(menu[0], "Run in app"); | ||||||
|  |     string_init_set_str(menu[1], "Pin"); | ||||||
|  |     string_init_set_str(menu[2], "Rename"); | ||||||
|  |     string_init_set_str(menu[3], "Delete"); | ||||||
|  | 
 | ||||||
|  |     ArchiveFile_t* selected = files_array_get(model->files, model->idx); | ||||||
|  | 
 | ||||||
|  |     if(!is_known_app(selected->type)) { | ||||||
|  |         string_set_str(menu[0], "---"); | ||||||
|  |         string_set_str(menu[1], "---"); | ||||||
|  |         string_set_str(menu[2], "---"); | ||||||
|  |     } else if(selected->fav) { | ||||||
|  |         string_set_str(menu[1], "Unpin"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < MENU_ITEMS; i++) { | ||||||
|  |         canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); | ||||||
|  |         string_clear(menu[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); | ||||||
|  |     canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); | ||||||
|  |     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); | ||||||
|  |     canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); | ||||||
|  |     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) { | ||||||
|  |     furi_assert(model); | ||||||
|  | 
 | ||||||
|  |     size_t array_size = files_array_size(model->files); | ||||||
|  |     bool scrollbar = array_size > 4; | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { | ||||||
|  |         string_t str_buff; | ||||||
|  |         char cstr_buff[MAX_NAME_LEN]; | ||||||
|  | 
 | ||||||
|  |         size_t idx = CLAMP(i + model->list_offset, array_size, 0); | ||||||
|  |         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); | ||||||
|  | 
 | ||||||
|  |         string_init_set(str_buff, file->name); | ||||||
|  |         string_right(str_buff, string_search_rchar(str_buff, '/') + 1); | ||||||
|  |         strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1); | ||||||
|  | 
 | ||||||
|  |         if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); | ||||||
|  | 
 | ||||||
|  |         string_clean(str_buff); | ||||||
|  |         string_set_str(str_buff, cstr_buff); | ||||||
|  | 
 | ||||||
|  |         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); | ||||||
|  | 
 | ||||||
|  |         if(model->idx == idx) { | ||||||
|  |             archive_draw_frame(canvas, i, scrollbar); | ||||||
|  |         } else { | ||||||
|  |             canvas_set_color(canvas, ColorBlack); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); | ||||||
|  |         canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||||
|  |         string_clear(str_buff); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(scrollbar) { | ||||||
|  |         elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->action == BrowserActionItemMenu) { | ||||||
|  |         render_item_menu(canvas, model); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) { | ||||||
|  |     furi_assert(model); | ||||||
|  | 
 | ||||||
|  |     const char* tab_name = ArchiveTabNames[model->tab_idx]; | ||||||
|  | 
 | ||||||
|  |     canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_box(canvas, 0, 0, 50, 13); | ||||||
|  |     canvas_draw_box(canvas, 107, 0, 20, 13); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_draw_frame(canvas, 1, 0, 50, 12); | ||||||
|  |     canvas_draw_line(canvas, 0, 1, 0, 11); | ||||||
|  |     canvas_draw_line(canvas, 1, 12, 49, 12); | ||||||
|  |     canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_frame(canvas, 108, 0, 20, 12); | ||||||
|  |     canvas_draw_line(canvas, 107, 1, 107, 11); | ||||||
|  |     canvas_draw_line(canvas, 108, 12, 126, 12); | ||||||
|  | 
 | ||||||
|  |     if(model->tab_idx > 0) { | ||||||
|  |         canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); | ||||||
|  |     } | ||||||
|  |     if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { | ||||||
|  |         canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_dot(canvas, 50, 0); | ||||||
|  |     canvas_draw_dot(canvas, 127, 0); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_view_render(Canvas* canvas, void* model) { | ||||||
|  |     ArchiveMainViewModel* m = model; | ||||||
|  | 
 | ||||||
|  |     archive_render_status_bar(canvas, model); | ||||||
|  | 
 | ||||||
|  |     if(files_array_size(m->files) > 0) { | ||||||
|  |         draw_list(canvas, m); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* archive_main_get_view(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     return main_view->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_show_file_menu(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             ArchiveFile_t* selected; | ||||||
|  |             selected = files_array_get(model->files, model->idx); | ||||||
|  |             model->action = BrowserActionItemMenu; | ||||||
|  |             model->menu_idx = 0; | ||||||
|  |             selected->fav = is_known_app(selected->type) ? archive_is_favorite( | ||||||
|  |                                                                string_get_cstr(main_view->path), | ||||||
|  |                                                                string_get_cstr(selected->name)) : | ||||||
|  |                                                            false; | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_close_file_menu(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             model->action = BrowserActionBrowse; | ||||||
|  |             model->menu_idx = 0; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_run_in_app( | ||||||
|  |     ArchiveMainView* main_view, | ||||||
|  |     ArchiveFile_t* selected, | ||||||
|  |     bool full_path_provided) { | ||||||
|  |     Loader* loader = furi_record_open("loader"); | ||||||
|  | 
 | ||||||
|  |     string_t full_path; | ||||||
|  | 
 | ||||||
|  |     if(!full_path_provided) { | ||||||
|  |         string_init_printf( | ||||||
|  |             full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name)); | ||||||
|  |     } else { | ||||||
|  |         string_init_set(full_path, selected->name); | ||||||
|  |     } | ||||||
|  |     loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); | ||||||
|  | 
 | ||||||
|  |     string_clear(full_path); | ||||||
|  |     furi_record_close("loader"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_file_menu_callback(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     ArchiveFile_t* selected = archive_get_current_file(main_view); | ||||||
|  |     const char* path = archive_get_path(main_view); | ||||||
|  |     const char* name = archive_get_name(main_view); | ||||||
|  | 
 | ||||||
|  |     uint8_t idx; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             idx = model->menu_idx; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     switch(idx) { | ||||||
|  |     case 0: | ||||||
|  |         if(is_known_app(selected->type)) { | ||||||
|  |             archive_run_in_app(main_view, selected, false); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 1: | ||||||
|  |         if(is_known_app(selected->type)) { | ||||||
|  |             if(!archive_is_favorite(path, name)) { | ||||||
|  |                 archive_set_name(main_view, string_get_cstr(selected->name)); | ||||||
|  |                 archive_add_to_favorites(path, name); | ||||||
|  |             } else { | ||||||
|  |                 // delete from favorites
 | ||||||
|  |                 archive_favorites_delete(path, name); | ||||||
|  |             } | ||||||
|  |             archive_close_file_menu(main_view); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         // open rename view
 | ||||||
|  |         if(is_known_app(selected->type)) { | ||||||
|  |             main_view->callback(ArchiveBrowserEventRename, main_view->context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case 3: | ||||||
|  |         // confirmation?
 | ||||||
|  |         archive_delete_file(main_view, main_view->path, selected->name); | ||||||
|  |         archive_close_file_menu(main_view); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         archive_close_file_menu(main_view); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     selected = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_switch_dir(ArchiveMainView* main_view, const char* path) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     furi_assert(path); | ||||||
|  | 
 | ||||||
|  |     string_set(main_view->path, path); | ||||||
|  |     archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path)); | ||||||
|  |     update_offset(main_view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_switch_tab(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             model->idx = 0; | ||||||
|  |             model->depth = 0; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_enter_dir(ArchiveMainView* main_view, string_t name) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  |     furi_assert(name); | ||||||
|  | 
 | ||||||
|  |     // update last index
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             model->last_idx[model->depth] = | ||||||
|  |                 CLAMP(model->idx, files_array_size(model->files) - 1, 0); | ||||||
|  |             model->idx = 0; | ||||||
|  |             model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     string_cat(main_view->path, "/"); | ||||||
|  |     string_cat(main_view->path, main_view->name); | ||||||
|  | 
 | ||||||
|  |     archive_switch_dir(main_view, string_get_cstr(main_view->path)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void archive_leave_dir(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/'); | ||||||
|  | 
 | ||||||
|  |     if(last_char_ptr) { | ||||||
|  |         size_t pos = last_char_ptr - string_get_cstr(main_view->path); | ||||||
|  |         string_left(main_view->path, pos); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0); | ||||||
|  |             model->idx = model->last_idx[model->depth]; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     archive_switch_dir(main_view, string_get_cstr(main_view->path)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool archive_view_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(event); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ArchiveMainView* main_view = context; | ||||||
|  | 
 | ||||||
|  |     BrowserActionEnum action; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             action = model->action; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     switch(action) { | ||||||
|  |     case BrowserActionItemMenu: | ||||||
|  | 
 | ||||||
|  |         if(event->type == InputTypeShort) { | ||||||
|  |             if(event->key == InputKeyUp || event->key == InputKeyDown) { | ||||||
|  |                 with_view_model( | ||||||
|  |                     main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |                         if(event->key == InputKeyUp) { | ||||||
|  |                             model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; | ||||||
|  |                         } else if(event->key == InputKeyDown) { | ||||||
|  |                             model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; | ||||||
|  |                         } | ||||||
|  |                         return true; | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(event->key == InputKeyOk) { | ||||||
|  |                 archive_file_menu_callback(main_view); | ||||||
|  |             } else if(event->key == InputKeyBack) { | ||||||
|  |                 archive_close_file_menu(main_view); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case BrowserActionBrowse: | ||||||
|  | 
 | ||||||
|  |         if(event->type == InputTypeShort) { | ||||||
|  |             if(event->key == InputKeyLeft) { | ||||||
|  |                 ArchiveTabEnum tab = archive_get_tab(main_view); | ||||||
|  |                 if(tab) { | ||||||
|  |                     archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0)); | ||||||
|  |                     archive_switch_tab(main_view); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyRight) { | ||||||
|  |                 ArchiveTabEnum tab = archive_get_tab(main_view); | ||||||
|  | 
 | ||||||
|  |                 if(tab < ArchiveTabTotal - 1) { | ||||||
|  |                     archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0)); | ||||||
|  |                     archive_switch_tab(main_view); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } else if(event->key == InputKeyBack) { | ||||||
|  |                 if(!archive_get_depth(main_view)) { | ||||||
|  |                     main_view->callback(ArchiveBrowserEventExit, main_view->context); | ||||||
|  |                 } else { | ||||||
|  |                     archive_leave_dir(main_view); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(event->key == InputKeyUp || event->key == InputKeyDown) { | ||||||
|  |             with_view_model( | ||||||
|  |                 main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |                     uint16_t num_elements = (uint16_t)files_array_size(model->files); | ||||||
|  |                     if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
|  |                         if(event->key == InputKeyUp) { | ||||||
|  |                             model->idx = ((model->idx - 1) + num_elements) % num_elements; | ||||||
|  |                         } else if(event->key == InputKeyDown) { | ||||||
|  |                             model->idx = (model->idx + 1) % num_elements; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             update_offset(main_view); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(event->key == InputKeyOk) { | ||||||
|  |             ArchiveFile_t* selected = archive_get_current_file(main_view); | ||||||
|  | 
 | ||||||
|  |             if(selected) { | ||||||
|  |                 archive_set_name(main_view, string_get_cstr(selected->name)); | ||||||
|  |                 if(selected->type == ArchiveFileTypeFolder) { | ||||||
|  |                     if(event->type == InputTypeShort) { | ||||||
|  |                         archive_enter_dir(main_view, main_view->name); | ||||||
|  |                     } else if(event->type == InputTypeLong) { | ||||||
|  |                         archive_show_file_menu(main_view); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     if(event->type == InputTypeShort) { | ||||||
|  |                         if(archive_get_tab(main_view) == ArchiveTabFavorites) { | ||||||
|  |                             if(is_known_app(selected->type)) { | ||||||
|  |                                 archive_run_in_app(main_view, selected, true); | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             archive_show_file_menu(main_view); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ArchiveMainView* main_view_alloc() { | ||||||
|  |     ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView)); | ||||||
|  |     main_view->view = view_alloc(); | ||||||
|  |     view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel)); | ||||||
|  |     view_set_context(main_view->view, main_view); | ||||||
|  |     view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render); | ||||||
|  |     view_set_input_callback(main_view->view, archive_view_input); | ||||||
|  | 
 | ||||||
|  |     string_init(main_view->name); | ||||||
|  |     string_init(main_view->path); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             files_array_init(model->files); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return main_view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void main_view_free(ArchiveMainView* main_view) { | ||||||
|  |     furi_assert(main_view); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (ArchiveMainViewModel * model) { | ||||||
|  |             files_array_clear(model->files); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     string_clear(main_view->name); | ||||||
|  |     string_clear(main_view->path); | ||||||
|  | 
 | ||||||
|  |     view_free(main_view->view); | ||||||
|  |     free(main_view); | ||||||
|  | } | ||||||
							
								
								
									
										117
									
								
								applications/archive/views/archive_main_view.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								applications/archive/views/archive_main_view.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/gui_i.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/canvas.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <furi.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include "../helpers/archive_files.h" | ||||||
|  | #include "../helpers/archive_favorites.h" | ||||||
|  | 
 | ||||||
|  | #define MAX_LEN_PX 110 | ||||||
|  | #define MAX_NAME_LEN 255 | ||||||
|  | #define FRAME_HEIGHT 12 | ||||||
|  | #define MENU_ITEMS 4 | ||||||
|  | #define MAX_DEPTH 32 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     ArchiveTabFavorites, | ||||||
|  |     ArchiveTabLFRFID, | ||||||
|  |     ArchiveTabSubGhz, | ||||||
|  |     ArchiveTabNFC, | ||||||
|  |     ArchiveTabIButton, | ||||||
|  |     ArchiveTabIrda, | ||||||
|  |     ArchiveTabBrowser, | ||||||
|  |     ArchiveTabTotal, | ||||||
|  | } ArchiveTabEnum; | ||||||
|  | 
 | ||||||
|  | static const char* known_ext[] = { | ||||||
|  |     [ArchiveFileTypeIButton] = ".ibtn", | ||||||
|  |     [ArchiveFileTypeNFC] = ".nfc", | ||||||
|  |     [ArchiveFileTypeSubGhz] = ".sub", | ||||||
|  |     [ArchiveFileTypeLFRFID] = ".rfid", | ||||||
|  |     [ArchiveFileTypeIrda] = ".ir", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline const char* get_tab_ext(ArchiveTabEnum tab) { | ||||||
|  |     switch(tab) { | ||||||
|  |     case ArchiveTabIButton: | ||||||
|  |         return known_ext[ArchiveFileTypeIButton]; | ||||||
|  |     case ArchiveTabNFC: | ||||||
|  |         return known_ext[ArchiveFileTypeNFC]; | ||||||
|  |     case ArchiveTabSubGhz: | ||||||
|  |         return known_ext[ArchiveFileTypeSubGhz]; | ||||||
|  |     case ArchiveTabLFRFID: | ||||||
|  |         return known_ext[ArchiveFileTypeLFRFID]; | ||||||
|  |     case ArchiveTabIrda: | ||||||
|  |         return known_ext[ArchiveFileTypeIrda]; | ||||||
|  |     default: | ||||||
|  |         return "*"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     ArchiveBrowserEventRename, | ||||||
|  |     ArchiveBrowserEventExit, | ||||||
|  |     ArchiveBrowserEventLeaveDir, | ||||||
|  | } ArchiveBrowserEvent; | ||||||
|  | 
 | ||||||
|  | typedef struct ArchiveMainView ArchiveMainView; | ||||||
|  | 
 | ||||||
|  | typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     BrowserActionBrowse, | ||||||
|  |     BrowserActionItemMenu, | ||||||
|  |     BrowserActionTotal, | ||||||
|  | } BrowserActionEnum; | ||||||
|  | 
 | ||||||
|  | struct ArchiveMainView { | ||||||
|  |     View* view; | ||||||
|  |     ArchiveMainViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | 
 | ||||||
|  |     string_t name; | ||||||
|  |     string_t path; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     ArchiveTabEnum tab_idx; | ||||||
|  |     BrowserActionEnum action; | ||||||
|  |     files_array_t files; | ||||||
|  | 
 | ||||||
|  |     uint8_t depth; | ||||||
|  |     uint8_t menu_idx; | ||||||
|  | 
 | ||||||
|  |     uint16_t idx; | ||||||
|  |     uint16_t last_idx[MAX_DEPTH]; | ||||||
|  |     uint16_t list_offset; | ||||||
|  | 
 | ||||||
|  | } ArchiveMainViewModel; | ||||||
|  | 
 | ||||||
|  | void archive_browser_set_callback( | ||||||
|  |     ArchiveMainView* main_view, | ||||||
|  |     ArchiveMainViewCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | View* archive_main_get_view(ArchiveMainView* main_view); | ||||||
|  | 
 | ||||||
|  | ArchiveMainView* main_view_alloc(); | ||||||
|  | void main_view_free(ArchiveMainView* main_view); | ||||||
|  | 
 | ||||||
|  | void archive_file_array_remove_selected(ArchiveMainView* main_view); | ||||||
|  | void archive_file_array_clean(ArchiveMainView* main_view); | ||||||
|  | 
 | ||||||
|  | void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name); | ||||||
|  | void archive_browser_update(ArchiveMainView* main_view); | ||||||
|  | 
 | ||||||
|  | size_t archive_file_array_size(ArchiveMainView* main_view); | ||||||
|  | ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view); | ||||||
|  | const char* archive_get_path(ArchiveMainView* main_view); | ||||||
|  | const char* archive_get_name(ArchiveMainView* main_view); | ||||||
|  | void archive_set_name(ArchiveMainView* main_view, const char* name); | ||||||
|  | 
 | ||||||
|  | static inline bool is_known_app(ArchiveFileTypeEnum type) { | ||||||
|  |     return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								applications/bt/bt_service/bt.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										25
									
								
								applications/bt/bt_service/bt.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,4 +1,5 @@ | |||||||
| #include "bt_i.h" | #include "bt_i.h" | ||||||
|  | #include "battery_service.h" | ||||||
| 
 | 
 | ||||||
| #define BT_SERVICE_TAG "BT" | #define BT_SERVICE_TAG "BT" | ||||||
| 
 | 
 | ||||||
| @ -21,6 +22,17 @@ static ViewPort* bt_statusbar_view_port_alloc() { | |||||||
|     return statusbar_view_port; |     return statusbar_view_port; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     string_t pin_str; | ||||||
|  |     string_init_printf(pin_str, "%06d", pin); | ||||||
|  |     dialog_message_set_text( | ||||||
|  |         bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter); | ||||||
|  |     dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL); | ||||||
|  |     dialog_message_show(bt->dialogs, bt->dialog_message); | ||||||
|  |     string_clear(pin_str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Bt* bt_alloc() { | Bt* bt_alloc() { | ||||||
|     Bt* bt = furi_alloc(sizeof(Bt)); |     Bt* bt = furi_alloc(sizeof(Bt)); | ||||||
|     // Load settings
 |     // Load settings
 | ||||||
| @ -40,13 +52,16 @@ Bt* bt_alloc() { | |||||||
|     bt->gui = furi_record_open("gui"); |     bt->gui = furi_record_open("gui"); | ||||||
|     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); |     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); | ||||||
| 
 | 
 | ||||||
|  |     // Dialogs
 | ||||||
|  |     bt->dialogs = furi_record_open("dialogs"); | ||||||
|  |     bt->dialog_message = dialog_message_alloc(); | ||||||
|  | 
 | ||||||
|     return bt; |     return bt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t bt_srv() { | int32_t bt_srv() { | ||||||
|     Bt* bt = bt_alloc(); |     Bt* bt = bt_alloc(); | ||||||
|     furi_record_create("bt", bt); |     furi_record_create("bt", bt); | ||||||
|     furi_hal_bt_init(); |  | ||||||
| 
 | 
 | ||||||
|     if(!furi_hal_bt_wait_startup()) { |     if(!furi_hal_bt_wait_startup()) { | ||||||
|         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed"); |         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed"); | ||||||
| @ -68,6 +83,14 @@ int32_t bt_srv() { | |||||||
|         if(message.type == BtMessageTypeUpdateStatusbar) { |         if(message.type == BtMessageTypeUpdateStatusbar) { | ||||||
|             // Update statusbar
 |             // Update statusbar
 | ||||||
|             view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); |             view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); | ||||||
|  |         } else if(message.type == BtMessageTypeUpdateBatteryLevel) { | ||||||
|  |             // Update battery level
 | ||||||
|  |             if(furi_hal_bt_is_alive()) { | ||||||
|  |                 battery_svc_update_level(message.data.battery_level); | ||||||
|  |             } | ||||||
|  |         } else if(message.type == BtMessageTypePinCodeShow) { | ||||||
|  |             // Display PIN code
 | ||||||
|  |             bt_pin_code_show_event_handler(bt, message.data.pin_code); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -1,3 +1,18 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef struct Bt Bt; | typedef struct Bt Bt; | ||||||
|  | 
 | ||||||
|  | void bt_update_battery_level(Bt* bt, uint8_t battery_level); | ||||||
|  | 
 | ||||||
|  | bool bt_pin_code_show(Bt* bt, uint32_t pin_code); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								applications/bt/bt_service/bt_api.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								applications/bt/bt_service/bt_api.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | #include "bt.h" | ||||||
|  | #include "bt_i.h" | ||||||
|  | 
 | ||||||
|  | void bt_update_battery_level(Bt* bt, uint8_t battery_level) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     BtMessage message = { | ||||||
|  |         .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level}; | ||||||
|  |     furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool bt_pin_code_show(Bt* bt, uint32_t pin_code) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     BtMessage message = {.type = BtMessageTypePinCodeShow, .data.pin_code = pin_code}; | ||||||
|  |     return osMessageQueuePut(bt->message_queue, &message, 0, 0) == osOK; | ||||||
|  | } | ||||||
| @ -9,15 +9,24 @@ | |||||||
| #include <gui/view_port.h> | #include <gui/view_port.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #include <applications/dialogs/dialogs.h> | ||||||
|  | 
 | ||||||
| #include "../bt_settings.h" | #include "../bt_settings.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     BtMessageTypeUpdateStatusbar, |     BtMessageTypeUpdateStatusbar, | ||||||
|  |     BtMessageTypeUpdateBatteryLevel, | ||||||
|  |     BtMessageTypePinCodeShow, | ||||||
| } BtMessageType; | } BtMessageType; | ||||||
| 
 | 
 | ||||||
|  | typedef union { | ||||||
|  |     uint32_t pin_code; | ||||||
|  |     uint8_t battery_level; | ||||||
|  | } BtMessageData; | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     BtMessageType type; |     BtMessageType type; | ||||||
|     void* param; |     BtMessageData data; | ||||||
| } BtMessage; | } BtMessage; | ||||||
| 
 | 
 | ||||||
| struct Bt { | struct Bt { | ||||||
| @ -26,4 +35,6 @@ struct Bt { | |||||||
|     osTimerId_t update_status_timer; |     osTimerId_t update_status_timer; | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewPort* statusbar_view_port; |     ViewPort* statusbar_view_port; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|  |     DialogMessage* dialog_message; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -68,10 +68,10 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { | |||||||
|                 canvas_draw_str(canvas, 73, item_text_y, "<"); |                 canvas_draw_str(canvas, 73, item_text_y, "<"); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             canvas_draw_str(canvas, 84, item_text_y, string_get_cstr(item->current_value_text)); |             canvas_draw_str(canvas, 80, item_text_y, string_get_cstr(item->current_value_text)); | ||||||
| 
 | 
 | ||||||
|             if(item->current_value_index < (item->values_count - 1)) { |             if(item->current_value_index < (item->values_count - 1)) { | ||||||
|                 canvas_draw_str(canvas, 113, item_text_y, ">"); |                 canvas_draw_str(canvas, 115, item_text_y, ">"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -235,6 +235,21 @@ void variable_item_list_free(VariableItemList* variable_item_list) { | |||||||
|     free(variable_item_list); |     free(variable_item_list); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void variable_item_list_clean(VariableItemList* variable_item_list) { | ||||||
|  |     furi_assert(variable_item_list); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         variable_item_list->view, (VariableItemListModel * model) { | ||||||
|  |             VariableItemArray_it_t it; | ||||||
|  |             for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); | ||||||
|  |                 VariableItemArray_next(it)) { | ||||||
|  |                 string_clean(VariableItemArray_ref(it)->current_value_text); | ||||||
|  |             } | ||||||
|  |             VariableItemArray_clean(model->items); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| View* variable_item_list_get_view(VariableItemList* variable_item_list) { | View* variable_item_list_get_view(VariableItemList* variable_item_list) { | ||||||
|     furi_assert(variable_item_list); |     furi_assert(variable_item_list); | ||||||
|     return variable_item_list->view; |     return variable_item_list->view; | ||||||
|  | |||||||
| @ -18,6 +18,9 @@ VariableItemList* variable_item_list_alloc(); | |||||||
|  * @param variable_item_list VariableItemList instance |  * @param variable_item_list VariableItemList instance | ||||||
|  */ |  */ | ||||||
| void variable_item_list_free(VariableItemList* variable_item_list); | void variable_item_list_free(VariableItemList* variable_item_list); | ||||||
|  | 
 | ||||||
|  | void variable_item_list_clean(VariableItemList* variable_item_list); | ||||||
|  | 
 | ||||||
| View* variable_item_list_get_view(VariableItemList* variable_item_list); | View* variable_item_list_get_view(VariableItemList* variable_item_list); | ||||||
| 
 | 
 | ||||||
| /** Add item to VariableItemList
 | /** Add item to VariableItemList
 | ||||||
|  | |||||||
| @ -118,6 +118,20 @@ static void widget_add_element(Widget* widget, WidgetElement* element) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void widget_add_string_multi_element( | ||||||
|  |     Widget* widget, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     Align horizontal, | ||||||
|  |     Align vertical, | ||||||
|  |     Font font, | ||||||
|  |     const char* text) { | ||||||
|  |     furi_assert(widget); | ||||||
|  |     WidgetElement* string_multi_element = | ||||||
|  |         widget_element_string_multi_create(x, y, horizontal, vertical, font, text); | ||||||
|  |     widget_add_element(widget, string_multi_element); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void widget_add_string_element( | void widget_add_string_element( | ||||||
|     Widget* widget, |     Widget* widget, | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|  | |||||||
| @ -26,6 +26,23 @@ void widget_clear(Widget* widget); | |||||||
|  */ |  */ | ||||||
| View* widget_get_view(Widget* widget); | View* widget_get_view(Widget* widget); | ||||||
| 
 | 
 | ||||||
|  | /** Add Multi String Element
 | ||||||
|  |  * @param widget Widget instance | ||||||
|  |  * @param x - x coordinate | ||||||
|  |  * @param y - y coordinate | ||||||
|  |  * @param horizontal - Align instance | ||||||
|  |  * @param vertical - Align instance | ||||||
|  |  * @param font Font instance | ||||||
|  |  */ | ||||||
|  | void widget_add_string_multi_element( | ||||||
|  |     Widget* widget, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     Align horizontal, | ||||||
|  |     Align vertical, | ||||||
|  |     Font font, | ||||||
|  |     const char* text); | ||||||
|  | 
 | ||||||
| /** Add String Element
 | /** Add String Element
 | ||||||
|  * @param widget Widget instance |  * @param widget Widget instance | ||||||
|  * @param x - x coordinate |  * @param x - x coordinate | ||||||
|  | |||||||
| @ -30,7 +30,18 @@ static bool gui_button_input(InputEvent* event, WidgetElement* element) { | |||||||
|     GuiButtonModel* model = element->model; |     GuiButtonModel* model = element->model; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if((event->type == InputTypeShort) && model->callback) { |     if(model->callback == NULL) return consumed; | ||||||
|  | 
 | ||||||
|  |     if(event->key == InputKeyOk && event->type == InputTypePress && | ||||||
|  |        model->button_type == GuiButtonTypeCenter) { | ||||||
|  |         model->callback(GuiButtonTypeCenterPress, model->context); | ||||||
|  |         consumed = true; | ||||||
|  |     } else if( | ||||||
|  |         event->key == InputKeyOk && event->type == InputTypeRelease && | ||||||
|  |         model->button_type == GuiButtonTypeCenter) { | ||||||
|  |         model->callback(GuiButtonTypeCenterRelease, model->context); | ||||||
|  |         consumed = true; | ||||||
|  |     } else if(event->type == InputTypeShort) { | ||||||
|         if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) { |         if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) { | ||||||
|             model->callback(model->button_type, model->context); |             model->callback(model->button_type, model->context); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ typedef enum { | |||||||
|     GuiButtonTypeLeft, |     GuiButtonTypeLeft, | ||||||
|     GuiButtonTypeCenter, |     GuiButtonTypeCenter, | ||||||
|     GuiButtonTypeRight, |     GuiButtonTypeRight, | ||||||
|  |     GuiButtonTypeCenterPress, | ||||||
|  |     GuiButtonTypeCenterRelease, | ||||||
| } GuiButtonType; | } GuiButtonType; | ||||||
| 
 | 
 | ||||||
| typedef void (*ButtonCallback)(GuiButtonType result, void* context); | typedef void (*ButtonCallback)(GuiButtonType result, void* context); | ||||||
| @ -28,6 +30,15 @@ struct WidgetElement { | |||||||
|     Widget* parent; |     Widget* parent; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* Create multi string element */ | ||||||
|  | WidgetElement* widget_element_string_multi_create( | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     Align horizontal, | ||||||
|  |     Align vertical, | ||||||
|  |     Font font, | ||||||
|  |     const char* text); | ||||||
|  | 
 | ||||||
| /* Create string element */ | /* Create string element */ | ||||||
| WidgetElement* widget_element_string_create( | WidgetElement* widget_element_string_create( | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|  | |||||||
| @ -0,0 +1,67 @@ | |||||||
|  | #include "widget_element_i.h" | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t x; | ||||||
|  |     uint8_t y; | ||||||
|  |     Align horizontal; | ||||||
|  |     Align vertical; | ||||||
|  |     Font font; | ||||||
|  |     string_t text; | ||||||
|  | } GuiStringMultiModel; | ||||||
|  | 
 | ||||||
|  | static void gui_string_multi_draw(Canvas* canvas, WidgetElement* element) { | ||||||
|  |     furi_assert(canvas); | ||||||
|  |     furi_assert(element); | ||||||
|  |     GuiStringMultiModel* model = element->model; | ||||||
|  | 
 | ||||||
|  |     if(string_size(model->text)) { | ||||||
|  |         canvas_set_font(canvas, model->font); | ||||||
|  |         elements_multiline_text_aligned( | ||||||
|  |             canvas, | ||||||
|  |             model->x, | ||||||
|  |             model->y, | ||||||
|  |             model->horizontal, | ||||||
|  |             model->vertical, | ||||||
|  |             string_get_cstr(model->text)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gui_string_multi_free(WidgetElement* gui_string) { | ||||||
|  |     furi_assert(gui_string); | ||||||
|  | 
 | ||||||
|  |     GuiStringMultiModel* model = gui_string->model; | ||||||
|  |     string_clear(model->text); | ||||||
|  |     free(gui_string->model); | ||||||
|  |     free(gui_string); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | WidgetElement* widget_element_string_multi_create( | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     Align horizontal, | ||||||
|  |     Align vertical, | ||||||
|  |     Font font, | ||||||
|  |     const char* text) { | ||||||
|  |     furi_assert(text); | ||||||
|  | 
 | ||||||
|  |     // Allocate and init model
 | ||||||
|  |     GuiStringMultiModel* model = furi_alloc(sizeof(GuiStringMultiModel)); | ||||||
|  |     model->x = x; | ||||||
|  |     model->y = y; | ||||||
|  |     model->horizontal = horizontal; | ||||||
|  |     model->vertical = vertical; | ||||||
|  |     model->font = font; | ||||||
|  |     string_init_set_str(model->text, text); | ||||||
|  | 
 | ||||||
|  |     // Allocate and init Element
 | ||||||
|  |     WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement)); | ||||||
|  |     gui_string->parent = NULL; | ||||||
|  |     gui_string->input = NULL; | ||||||
|  |     gui_string->draw = gui_string_multi_draw; | ||||||
|  |     gui_string->free = gui_string_multi_free; | ||||||
|  |     gui_string->model = model; | ||||||
|  | 
 | ||||||
|  |     return gui_string; | ||||||
|  | } | ||||||
| @ -25,9 +25,9 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s | |||||||
|             sizeof(buf), |             sizeof(buf), | ||||||
|             "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", |             "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_address_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||||
|             message->address, |             message->address, | ||||||
|             irda_get_protocol_command_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|             message->command, |             message->command, | ||||||
|             message->repeat ? " R" : ""); |             message->repeat ? " R" : ""); | ||||||
|         cli_write(cli, (uint8_t*)buf, buf_cnt); |         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||||
| @ -98,6 +98,20 @@ static bool parse_message(const char* str, IrdaMessage* message) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     uint32_t address_length = irda_get_protocol_address_length(protocol); | ||||||
|  |     uint32_t address_mask = (1LU << address_length) - 1; | ||||||
|  |     if(address != (address & address_mask)) { | ||||||
|  |         printf("Address out of range (mask 0x%08lX): 0x%lX\r\n", address_mask, address); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint32_t command_length = irda_get_protocol_command_length(protocol); | ||||||
|  |     uint32_t command_mask = (1LU << command_length) - 1; | ||||||
|  |     if(command != (command & command_mask)) { | ||||||
|  |         printf("Command out of range (mask 0x%08lX): 0x%lX\r\n", command_mask, command); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     message->protocol = protocol; |     message->protocol = protocol; | ||||||
|     message->address = address; |     message->address = address; | ||||||
|     message->command = command; |     message->command = command; | ||||||
|  | |||||||
| @ -45,9 +45,9 @@ size_t IrdaAppFileParser::stringify_message( | |||||||
|         "%.31s %.31s A:%0*lX C:%0*lX\n", |         "%.31s %.31s A:%0*lX C:%0*lX\n", | ||||||
|         name, |         name, | ||||||
|         irda_get_protocol_name(protocol), |         irda_get_protocol_name(protocol), | ||||||
|         irda_get_protocol_address_length(protocol), |         ROUND_UP_TO(irda_get_protocol_address_length(protocol), 4), | ||||||
|         message.address, |         message.address, | ||||||
|         irda_get_protocol_command_length(protocol), |         ROUND_UP_TO(irda_get_protocol_command_length(protocol), 4), | ||||||
|         message.command); |         message.command); | ||||||
| 
 | 
 | ||||||
|     furi_assert(written < buf_size); |     furi_assert(written < buf_size); | ||||||
| @ -162,8 +162,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> | |||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int address_length = irda_get_protocol_address_length(protocol); |     uint32_t address_length = irda_get_protocol_address_length(protocol); | ||||||
|     uint32_t address_mask = (1LU << (4 * address_length)) - 1; |     uint32_t address_mask = (1LU << address_length) - 1; | ||||||
|     if(address != (address & address_mask)) { |     if(address != (address & address_mask)) { | ||||||
|         size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); |         size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); | ||||||
|         FURI_LOG_E( |         FURI_LOG_E( | ||||||
| @ -176,8 +176,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal> | |||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int command_length = irda_get_protocol_command_length(protocol); |     uint32_t command_length = irda_get_protocol_command_length(protocol); | ||||||
|     uint32_t command_mask = (1LU << (4 * command_length)) - 1; |     uint32_t command_mask = (1LU << command_length) - 1; | ||||||
|     if(command != (command & command_mask)) { |     if(command != (command & command_mask)) { | ||||||
|         size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); |         size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30); | ||||||
|         FURI_LOG_E( |         FURI_LOG_E( | ||||||
|  | |||||||
| @ -29,9 +29,9 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", |                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", | ||||||
|                 remote_manager->get_button_name(app->get_current_button()).c_str(), |                 remote_manager->get_button_name(app->get_current_button()).c_str(), | ||||||
|                 irda_get_protocol_name(message->protocol), |                 irda_get_protocol_name(message->protocol), | ||||||
|                 irda_get_protocol_address_length(message->protocol), |                 ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||||
|                 message->address, |                 message->address, | ||||||
|                 irda_get_protocol_command_length(message->protocol), |                 ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|                 message->command); |                 message->command); | ||||||
|         } else { |         } else { | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | |||||||
|             0, |             0, | ||||||
|             "%.4s_%0*lX", |             "%.4s_%0*lX", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_command_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|             message->command); |             message->command); | ||||||
|     } else { |     } else { | ||||||
|         auto raw_signal = signal.get_raw_signal(); |         auto raw_signal = signal.get_raw_signal(); | ||||||
|  | |||||||
| @ -27,9 +27,9 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             1, |             1, | ||||||
|             "A: 0x%0*lX\nC: 0x%0*lX\n", |             "A: 0x%0*lX\nC: 0x%0*lX\n", | ||||||
|             irda_get_protocol_address_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||||
|             message->address, |             message->address, | ||||||
|             irda_get_protocol_command_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|             message->command); |             message->command); | ||||||
|         dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter); |         dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter); | ||||||
|         dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); |         dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); | ||||||
|  | |||||||
| @ -64,18 +64,18 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s | |||||||
|             sizeof(irda_monitor->display_text), |             sizeof(irda_monitor->display_text), | ||||||
|             "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", |             "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_address_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||||
|             message->address, |             message->address, | ||||||
|             irda_get_protocol_command_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|             message->command, |             message->command, | ||||||
|             message->repeat ? " R" : ""); |             message->repeat ? " R" : ""); | ||||||
|         view_port_update(irda_monitor->view_port); |         view_port_update(irda_monitor->view_port); | ||||||
|         printf( |         printf( | ||||||
|             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", |             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_address_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||||
|             message->address, |             message->address, | ||||||
|             irda_get_protocol_command_length(message->protocol), |             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||||
|             message->command, |             message->command, | ||||||
|             message->repeat ? " R" : ""); |             message->repeat ? " R" : ""); | ||||||
|     } else { |     } else { | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ | |||||||
| #include <stm32wbxx.h> | #include <stm32wbxx.h> | ||||||
| 
 | 
 | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
|  | #include <applications/bt/bt_service/bt.h> | ||||||
| 
 | 
 | ||||||
| #define POWER_OFF_TIMEOUT 30 | #define POWER_OFF_TIMEOUT 30 | ||||||
| 
 | 
 | ||||||
| @ -39,6 +40,7 @@ struct Power { | |||||||
| 
 | 
 | ||||||
|     ValueMutex* menu_vm; |     ValueMutex* menu_vm; | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|  |     Bt* bt; | ||||||
|     MenuItem* menu; |     MenuItem* menu; | ||||||
| 
 | 
 | ||||||
|     PowerState state; |     PowerState state; | ||||||
| @ -108,6 +110,8 @@ Power* power_alloc() { | |||||||
|     power->cli = furi_record_open("cli"); |     power->cli = furi_record_open("cli"); | ||||||
|     power_cli_init(power->cli, power); |     power_cli_init(power->cli, power); | ||||||
| 
 | 
 | ||||||
|  |     power->bt = furi_record_open("bt"); | ||||||
|  | 
 | ||||||
|     power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14)); |     power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14)); | ||||||
|     menu_item_subitem_add( |     menu_item_subitem_add( | ||||||
|         power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power)); |         power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power)); | ||||||
| @ -206,13 +210,15 @@ int32_t power_srv(void* p) { | |||||||
|         power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); }); |         power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); }); | ||||||
| 
 | 
 | ||||||
|     furi_record_create("power", power); |     furi_record_create("power", power); | ||||||
| 
 |     uint8_t battery_level = 0; | ||||||
|  |     uint8_t battery_level_prev = 0; | ||||||
|     while(1) { |     while(1) { | ||||||
|         bool battery_low = false; |         bool battery_low = false; | ||||||
| 
 | 
 | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             power->info_view, (PowerInfoModel * model) { |             power->info_view, (PowerInfoModel * model) { | ||||||
|                 model->charge = furi_hal_power_get_pct(); |                 model->charge = furi_hal_power_get_pct(); | ||||||
|  |                 battery_level = model->charge; | ||||||
|                 model->health = furi_hal_power_get_bat_health_pct(); |                 model->health = furi_hal_power_get_bat_health_pct(); | ||||||
|                 model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); |                 model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); | ||||||
|                 model->capacity_full = furi_hal_power_get_battery_full_capacity(); |                 model->capacity_full = furi_hal_power_get_battery_full_capacity(); | ||||||
| @ -258,6 +264,11 @@ int32_t power_srv(void* p) { | |||||||
| 
 | 
 | ||||||
|         power_charging_indication_handler(power, notifications); |         power_charging_indication_handler(power, notifications); | ||||||
| 
 | 
 | ||||||
|  |         if(battery_level_prev != battery_level) { | ||||||
|  |             battery_level_prev = battery_level; | ||||||
|  |             bt_update_battery_level(power->bt, battery_level); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         view_port_update(power->battery_view_port); |         view_port_update(power->battery_view_port); | ||||||
| 
 | 
 | ||||||
|         osDelay(1024); |         osDelay(1024); | ||||||
|  | |||||||
| @ -1,10 +1,12 @@ | |||||||
| ADD_SCENE(subghz, start, Start) | ADD_SCENE(subghz, start, Start) | ||||||
| ADD_SCENE(subghz, receiver, Receiver) | ADD_SCENE(subghz, receiver, Receiver) | ||||||
|  | ADD_SCENE(subghz, receiver_config, ReceiverConfig) | ||||||
|  | ADD_SCENE(subghz, receiver_info, ReceiverInfo) | ||||||
| ADD_SCENE(subghz, save_name, SaveName) | ADD_SCENE(subghz, save_name, SaveName) | ||||||
| ADD_SCENE(subghz, save_success, SaveSuccess) | ADD_SCENE(subghz, save_success, SaveSuccess) | ||||||
| ADD_SCENE(subghz, saved, Saved) | ADD_SCENE(subghz, saved, Saved) | ||||||
| ADD_SCENE(subghz, transmitter, Transmitter) | ADD_SCENE(subghz, transmitter, Transmitter) | ||||||
| ADD_SCENE(subghz, no_man, NoMan) | ADD_SCENE(subghz, show_error, ShowError) | ||||||
| ADD_SCENE(subghz, test, Test) | ADD_SCENE(subghz, test, Test) | ||||||
| ADD_SCENE(subghz, test_static, TestStatic) | ADD_SCENE(subghz, test_static, TestStatic) | ||||||
| ADD_SCENE(subghz, test_carrier, TestCarrier) | ADD_SCENE(subghz, test_carrier, TestCarrier) | ||||||
|  | |||||||
| @ -1,21 +1,102 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include "../views/subghz_receiver.h" | #include "../views/subghz_receiver.h" | ||||||
| 
 | 
 | ||||||
|  | static void subghz_scene_receiver_update_statusbar(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     char frequency_str[20]; | ||||||
|  |     char preset_str[10]; | ||||||
|  |     string_t history_stat_str; | ||||||
|  |     string_init(history_stat_str); | ||||||
|  |     if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { | ||||||
|  |         snprintf( | ||||||
|  |             frequency_str, | ||||||
|  |             sizeof(frequency_str), | ||||||
|  |             "%03ld.%02ld", | ||||||
|  |             subghz->txrx->frequency / 1000000 % 1000, | ||||||
|  |             subghz->txrx->frequency / 10000 % 100); | ||||||
|  |         if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || | ||||||
|  |            subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { | ||||||
|  |             snprintf(preset_str, sizeof(preset_str), "AM"); | ||||||
|  |         } else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) { | ||||||
|  |             snprintf(preset_str, sizeof(preset_str), "FM"); | ||||||
|  |         } else { | ||||||
|  |             furi_check(0); | ||||||
|  |         } | ||||||
|  |         subghz_receiver_add_data_statusbar( | ||||||
|  |             subghz->subghz_receiver, frequency_str, preset_str, string_get_cstr(history_stat_str)); | ||||||
|  |     } else { | ||||||
|  |         subghz_receiver_add_data_statusbar( | ||||||
|  |             subghz->subghz_receiver, string_get_cstr(history_stat_str), "", ""); | ||||||
|  |         subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|  |     } | ||||||
|  |     string_clear(history_stat_str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) { | void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); |     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     string_t str_buff; | ||||||
|  |     string_init(str_buff); | ||||||
|  | 
 | ||||||
|  |     if(subghz_history_add_to_history( | ||||||
|  |            subghz->txrx->history, parser, subghz->txrx->frequency, subghz->txrx->preset)) { | ||||||
|  |         subghz_protocol_reset(subghz->txrx->protocol); | ||||||
|  |         string_clean(str_buff); | ||||||
|  |         subghz_history_get_text_item_menu( | ||||||
|  |             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); | ||||||
|  |         subghz_receiver_add_item_to_menu( | ||||||
|  |             subghz->subghz_receiver, | ||||||
|  |             string_get_cstr(str_buff), | ||||||
|  |             subghz_history_get_type_protocol( | ||||||
|  |                 subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); | ||||||
|  |         subghz_scene_receiver_update_statusbar(subghz); | ||||||
|  |     } | ||||||
|  |     string_clear(str_buff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const void subghz_scene_receiver_on_enter(void* context) { | const void subghz_scene_receiver_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     SubghzReceiver* subghz_receiver = subghz->subghz_receiver; |  | ||||||
| 
 | 
 | ||||||
|     subghz_receiver_set_callback(subghz_receiver, subghz_scene_receiver_callback, subghz); |     string_t str_buff; | ||||||
|  |     string_init(str_buff); | ||||||
|  | 
 | ||||||
|  |     //Load history to receiver
 | ||||||
|  |     subghz_receiver_exit(subghz->subghz_receiver); | ||||||
|  |     for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { | ||||||
|  |         string_clean(str_buff); | ||||||
|  |         subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); | ||||||
|  |         subghz_receiver_add_item_to_menu( | ||||||
|  |             subghz->subghz_receiver, | ||||||
|  |             string_get_cstr(str_buff), | ||||||
|  |             subghz_history_get_type_protocol(subghz->txrx->history, i)); | ||||||
|  |     } | ||||||
|  |     string_clear(str_buff); | ||||||
|  |     subghz_scene_receiver_update_statusbar(subghz); | ||||||
|  |     subghz_receiver_set_callback(subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||||
|  |     subghz_protocol_enable_dump( | ||||||
|  |         subghz->txrx->protocol, subghz_scene_add_to_history_callback, subghz); | ||||||
| 
 | 
 | ||||||
|     subghz_receiver_set_protocol(subghz_receiver, subghz->protocol_result, subghz->protocol); |  | ||||||
|     subghz_receiver_set_worker(subghz_receiver, subghz->worker); |  | ||||||
|     subghz->state_notifications = NOTIFICATION_RX_STATE; |     subghz->state_notifications = NOTIFICATION_RX_STATE; | ||||||
|  |     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |         subghz_rx_end(subghz->txrx->worker); | ||||||
|  |         //subghz_sleep();
 | ||||||
|  |         subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |     }; | ||||||
|  |     if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { | ||||||
|  |         subghz_begin(subghz->txrx->preset); | ||||||
|  |         subghz_rx(subghz->txrx->worker, subghz->txrx->frequency); | ||||||
|  |         subghz->txrx->txrx_state = SubGhzTxRxStateRx; | ||||||
|  |     } | ||||||
|  |     if(subghz->txrx->idx_menu_chosen != 0) { | ||||||
|  |         subghz_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver); |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -24,51 +105,43 @@ const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubghzReceverEventSave: |  | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |  | ||||||
|             subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver); |  | ||||||
|             subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver); |  | ||||||
|             subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver); |  | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |  | ||||||
|             return true; |  | ||||||
|             break; |  | ||||||
|         case SubghzReceverEventBack: |         case SubghzReceverEventBack: | ||||||
|             scene_manager_previous_scene(subghz->scene_manager); |             // Stop CC1101 Rx
 | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |                 subghz_rx_end(subghz->txrx->worker); | ||||||
|  |                 subghz_sleep(); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             }; | ||||||
|  |             subghz_history_clean(subghz->txrx->history); | ||||||
|  |             subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||||
|  |             subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||||
|  |             subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; | ||||||
|  |             subghz->txrx->idx_menu_chosen = 0; | ||||||
|  |             subghz_protocol_enable_dump(subghz->txrx->protocol, NULL, subghz); | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|  |                 subghz->scene_manager, SubGhzSceneStart); | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
|         case SubghzReceverEventSendStart: |         case SubghzReceverEventOK: | ||||||
|             subghz->state_notifications = NOTIFICATION_TX_STATE; |             subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver); | ||||||
|             subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||||
|             subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver); |  | ||||||
|             subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver); |  | ||||||
|             subghz_transmitter_tx_start(subghz); |  | ||||||
|             return true; |  | ||||||
|             break; |  | ||||||
|         case SubghzReceverEventSendStop: |  | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |  | ||||||
|             subghz_transmitter_tx_stop(subghz); |  | ||||||
|             return true; |  | ||||||
|             break; |  | ||||||
|         case SubghzReceverEventMain: |  | ||||||
|             subghz->state_notifications = NOTIFICATION_RX_STATE; |  | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
|         case SubghzReceverEventConfig: |         case SubghzReceverEventConfig: | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|             return true; |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); | ||||||
|             break; |  | ||||||
|         case SubghzReceverEventSendHistoryFull: |  | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |  | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||||
|  |             subghz_hopper_update(subghz->txrx); | ||||||
|  |             subghz_scene_receiver_update_statusbar(subghz); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         switch(subghz->state_notifications) { |         switch(subghz->state_notifications) { | ||||||
|         case NOTIFICATION_TX_STATE: |  | ||||||
|             notification_message(subghz->notifications, &sequence_blink_red_10); |  | ||||||
|             break; |  | ||||||
|         case NOTIFICATION_RX_STATE: |         case NOTIFICATION_RX_STATE: | ||||||
|             notification_message(subghz->notifications, &sequence_blink_blue_10); |             notification_message(subghz->notifications, &sequence_blink_blue_10); | ||||||
|             break; |             break; | ||||||
|  | |||||||
							
								
								
									
										156
									
								
								applications/subghz/scenes/subghz_scene_receiver_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								applications/subghz/scenes/subghz_scene_receiver_config.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | |||||||
|  | #include "../subghz_i.h" | ||||||
|  | 
 | ||||||
|  | #define PRESET_COUNT 3 | ||||||
|  | const char* const preset_text[PRESET_COUNT] = { | ||||||
|  |     "AM270", | ||||||
|  |     "AM650", | ||||||
|  |     "FM", | ||||||
|  | }; | ||||||
|  | const uint32_t preset_value[PRESET_COUNT] = { | ||||||
|  |     FuriHalSubGhzPresetOok270Async, /** OOK, bandwidth 270kHz, asynchronous */ | ||||||
|  |     FuriHalSubGhzPresetOok650Async, /** OOK, bandwidth 650kHz, asynchronous */ | ||||||
|  |     FuriHalSubGhzPreset2FSKAsync, /** FM, asynchronous */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define HOPPING_COUNT 2 | ||||||
|  | const char* const hopping_text[HOPPING_COUNT] = { | ||||||
|  |     "OFF", | ||||||
|  |     "ON", | ||||||
|  | }; | ||||||
|  | const uint32_t hopping_value[HOPPING_COUNT] = { | ||||||
|  |     SubGhzHopperStateOFF, | ||||||
|  |     SubGhzHopperStateRunnig, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | uint8_t subghz_scene_receiver_config_uint32_value_index( | ||||||
|  |     const uint32_t value, | ||||||
|  |     const uint32_t values[], | ||||||
|  |     uint8_t values_count) { | ||||||
|  |     int64_t last_value = INT64_MIN; | ||||||
|  |     uint8_t index = 0; | ||||||
|  |     for(uint8_t i = 0; i < values_count; i++) { | ||||||
|  |         if((value >= last_value) && (value <= values[i])) { | ||||||
|  |             index = i; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         last_value = values[i]; | ||||||
|  |     } | ||||||
|  |     return index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t subghz_scene_receiver_config_hopper_value_index( | ||||||
|  |     const uint32_t value, | ||||||
|  |     const uint32_t values[], | ||||||
|  |     uint8_t values_count, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  | 
 | ||||||
|  |     if(value == values[0]) { | ||||||
|  |         return 0; | ||||||
|  |     } else { | ||||||
|  |         variable_item_set_current_value_text( | ||||||
|  |             (VariableItem*)scene_manager_get_scene_state( | ||||||
|  |                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||||
|  |             " -----"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { | ||||||
|  |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { | ||||||
|  |         variable_item_set_current_value_text(item, subghz_frequencies_text[index]); | ||||||
|  |         subghz->txrx->frequency = subghz_frequencies[index]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_scene_receiver_config_set_preset(VariableItem* item) { | ||||||
|  |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, preset_text[index]); | ||||||
|  |     subghz->txrx->preset = preset_value[index]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) { | ||||||
|  |     SubGhz* subghz = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, hopping_text[index]); | ||||||
|  |     if(hopping_value[index] == SubGhzHopperStateOFF) { | ||||||
|  |         variable_item_set_current_value_text( | ||||||
|  |             (VariableItem*)scene_manager_get_scene_state( | ||||||
|  |                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||||
|  |             subghz_frequencies_text[subghz_frequencies_433_92]); | ||||||
|  |         subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||||
|  |     } else { | ||||||
|  |         variable_item_set_current_value_text( | ||||||
|  |             (VariableItem*)scene_manager_get_scene_state( | ||||||
|  |                 subghz->scene_manager, SubGhzSceneReceiverConfig), | ||||||
|  |             " -----"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     subghz->txrx->hopper_state = hopping_value[index]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void subghz_scene_receiver_config_on_enter(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     VariableItem* item; | ||||||
|  |     uint8_t value_index; | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         subghz->variable_item_list, | ||||||
|  |         "Frequency:", | ||||||
|  |         subghz_frequencies_count, | ||||||
|  |         subghz_scene_receiver_config_set_frequency, | ||||||
|  |         subghz); | ||||||
|  |     value_index = subghz_scene_receiver_config_uint32_value_index( | ||||||
|  |         subghz->txrx->frequency, subghz_frequencies, subghz_frequencies_count); | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); | ||||||
|  |     variable_item_set_current_value_index(item, value_index); | ||||||
|  |     variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         subghz->variable_item_list, | ||||||
|  |         "Hopping:", | ||||||
|  |         HOPPING_COUNT, | ||||||
|  |         subghz_scene_receiver_config_set_hopping_runing, | ||||||
|  |         subghz); | ||||||
|  |     value_index = subghz_scene_receiver_config_hopper_value_index( | ||||||
|  |         subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); | ||||||
|  |     variable_item_set_current_value_index(item, value_index); | ||||||
|  |     variable_item_set_current_value_text(item, hopping_text[value_index]); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         subghz->variable_item_list, | ||||||
|  |         "Modulation:", | ||||||
|  |         PRESET_COUNT, | ||||||
|  |         subghz_scene_receiver_config_set_preset, | ||||||
|  |         subghz); | ||||||
|  |     value_index = subghz_scene_receiver_config_uint32_value_index( | ||||||
|  |         subghz->txrx->preset, preset_value, PRESET_COUNT); | ||||||
|  |     variable_item_set_current_value_index(item, value_index); | ||||||
|  |     variable_item_set_current_value_text(item, preset_text[value_index]); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewVariableItemList); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     //SubGhz* subghz = context;
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void subghz_scene_receiver_config_on_exit(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     variable_item_list_clean(subghz->variable_item_list); | ||||||
|  | } | ||||||
							
								
								
									
										163
									
								
								applications/subghz/scenes/subghz_scene_receiver_info.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								applications/subghz/scenes/subghz_scene_receiver_info.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,163 @@ | |||||||
|  | #include "../subghz_i.h" | ||||||
|  | 
 | ||||||
|  | void subghz_scene_receiver_info_callback(GuiButtonType result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     view_dispatcher_send_custom_event(subghz->view_dispatcher, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool subghz_scene_receiver_info_update_parser(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     subghz->txrx->protocol_result = subghz_protocol_get_by_name( | ||||||
|  |         subghz->txrx->protocol, | ||||||
|  |         subghz_history_get_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||||
|  | 
 | ||||||
|  |     if(subghz->txrx->protocol_result->to_load_protocol != NULL) { | ||||||
|  |         subghz->txrx->protocol_result->to_load_protocol( | ||||||
|  |             subghz->txrx->protocol_result, | ||||||
|  |             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||||
|  |         subghz->txrx->frequency = | ||||||
|  |             subghz_history_get_frequency(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||||
|  |         subghz->txrx->preset = | ||||||
|  |             subghz_history_get_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void subghz_scene_receiver_info_on_enter(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  | 
 | ||||||
|  |     if(subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|  |         char buffer_str[16]; | ||||||
|  |         snprintf( | ||||||
|  |             buffer_str, | ||||||
|  |             sizeof(buffer_str), | ||||||
|  |             "%03ld.%02ld", | ||||||
|  |             subghz->txrx->frequency / 1000000 % 1000, | ||||||
|  |             subghz->txrx->frequency / 10000 % 100); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); | ||||||
|  |         if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || | ||||||
|  |            subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { | ||||||
|  |             snprintf(buffer_str, sizeof(buffer_str), "AM"); | ||||||
|  |         } else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) { | ||||||
|  |             snprintf(buffer_str, sizeof(buffer_str), "FM"); | ||||||
|  |         } else { | ||||||
|  |             furi_check(0); | ||||||
|  |         } | ||||||
|  |         widget_add_string_element( | ||||||
|  |             subghz->widget, 113, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); | ||||||
|  |         string_t text; | ||||||
|  |         string_init(text); | ||||||
|  |         subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text); | ||||||
|  |         widget_add_string_multi_element( | ||||||
|  |             subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); | ||||||
|  |         string_clear(text); | ||||||
|  | 
 | ||||||
|  |         if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string && | ||||||
|  |            strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { | ||||||
|  |             widget_add_button_element( | ||||||
|  |                 subghz->widget, | ||||||
|  |                 GuiButtonTypeRight, | ||||||
|  |                 "Save", | ||||||
|  |                 subghz_scene_receiver_info_callback, | ||||||
|  |                 subghz); | ||||||
|  |             widget_add_button_element( | ||||||
|  |                 subghz->widget, | ||||||
|  |                 GuiButtonTypeCenter, | ||||||
|  |                 "Send", | ||||||
|  |                 subghz_scene_receiver_info_callback, | ||||||
|  |                 subghz); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |         widget_add_icon_element(subghz->widget, 32, 12, &I_DolphinFirstStart7_61x51); | ||||||
|  |         widget_add_string_element( | ||||||
|  |             subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GuiButtonTypeCenterPress) { | ||||||
|  |             //CC1101 Stop RX -> Start TX
 | ||||||
|  |             subghz->state_notifications = NOTIFICATION_TX_STATE; | ||||||
|  |             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||||
|  |                 subghz->txrx->hopper_state = SubGhzHopperStatePause; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |                 subghz_rx_end(subghz->txrx->worker); | ||||||
|  |                 //subghz_sleep();
 | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             } | ||||||
|  |             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { | ||||||
|  |                 subghz_tx_start(subghz); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateTx; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } else if(event.event == GuiButtonTypeCenterRelease) { | ||||||
|  |             //CC1101 Stop Tx -> Start RX
 | ||||||
|  |             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |                 subghz_tx_stop(subghz); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { | ||||||
|  |                 subghz_begin(subghz->txrx->preset); | ||||||
|  |                 subghz_rx(subghz->txrx->worker, subghz->txrx->frequency); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateRx; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { | ||||||
|  |                 subghz->txrx->hopper_state = SubGhzHopperStateRunnig; | ||||||
|  |             } | ||||||
|  |             subghz->state_notifications = NOTIFICATION_RX_STATE; | ||||||
|  |             return true; | ||||||
|  |         } else if(event.event == GuiButtonTypeRight) { | ||||||
|  |             //CC1101 Stop RX -> Save
 | ||||||
|  |             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|  |             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||||
|  |                 subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |                 subghz_rx_end(subghz->txrx->worker); | ||||||
|  |                 subghz_sleep(); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             } | ||||||
|  |             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string && | ||||||
|  |                strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { | ||||||
|  |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||||
|  |             subghz_hopper_update(subghz->txrx); | ||||||
|  |         } | ||||||
|  |         switch(subghz->state_notifications) { | ||||||
|  |         case NOTIFICATION_TX_STATE: | ||||||
|  |             notification_message(subghz->notifications, &sequence_blink_red_10); | ||||||
|  |             break; | ||||||
|  |         case NOTIFICATION_RX_STATE: | ||||||
|  |             notification_message(subghz->notifications, &sequence_blink_blue_10); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void subghz_scene_receiver_info_on_exit(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  |     widget_clear(subghz->widget); | ||||||
|  | } | ||||||
| @ -35,11 +35,13 @@ const bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent even | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { |         if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { | ||||||
|             if(subghz_save_protocol_to_file(subghz, subghz->text_store)) { |             if(strcmp(subghz->text_store, "") && | ||||||
|  |                subghz_save_protocol_to_file(subghz, subghz->text_store)) { | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); | ||||||
|                 return true; |                 return true; | ||||||
|             } else { |             } else { | ||||||
|                 //Error save
 |                 string_set(subghz->error_str, "No name file"); | ||||||
|  |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -8,15 +8,18 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexCAME12bit, |     SubmenuIndexCAME12bit, | ||||||
|     SubmenuIndexCAME24bit, |     SubmenuIndexCAME24bit, | ||||||
|     SubmenuIndexNeroSketch, |     SubmenuIndexNeroSketch, | ||||||
|  |     SubmenuIndexNeroRadio, | ||||||
|     SubmenuIndexGateTX, |     SubmenuIndexGateTX, | ||||||
|     SubmenuIndexDoorHan, |     SubmenuIndexDoorHan, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_set_type_submenu_to_find_protocol(void* context, const char* protocol_name) { | bool subghz_scene_set_type_submenu_to_find_protocol(void* context, const char* protocol_name) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     subghz->protocol_result = subghz_protocol_get_by_name(subghz->protocol, protocol_name); |     subghz->txrx->protocol_result = | ||||||
|     if(subghz->protocol_result == NULL) { |         subghz_protocol_get_by_name(subghz->txrx->protocol, protocol_name); | ||||||
|         //show error
 |     if(subghz->txrx->protocol_result == NULL) { | ||||||
|  |         string_set(subghz->error_str, "Protocol not found"); | ||||||
|  |         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| @ -62,6 +65,8 @@ const void subghz_scene_set_type_on_enter(void* context) { | |||||||
|         subghz); |         subghz); | ||||||
|     // submenu_add_item(
 |     // submenu_add_item(
 | ||||||
|     //     subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
 |     //     subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
 | ||||||
|  |     // submenu_add_item(
 | ||||||
|  |     //     subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
 | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         subghz->submenu, |         subghz->submenu, | ||||||
|         "Gate TX_433", |         "Gate TX_433", | ||||||
| @ -90,81 +95,86 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event | |||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubmenuIndexPricenton: |         case SubmenuIndexPricenton: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Princeton")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Princeton")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 24; |                 subghz->txrx->protocol_result->code_last_count_bit = 24; | ||||||
|                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|                 subghz->protocol_result->code_last_found = key; |                 subghz->txrx->protocol_result->code_last_found = key; | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo12bit: |         case SubmenuIndexNiceFlo12bit: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 12; |                 subghz->txrx->protocol_result->code_last_count_bit = 12; | ||||||
|                 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |                 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||||
|                 subghz->protocol_result->code_last_found = key; |                 subghz->txrx->protocol_result->code_last_found = key; | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNiceFlo24bit: |         case SubmenuIndexNiceFlo24bit: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 24; |                 subghz->txrx->protocol_result->code_last_count_bit = 24; | ||||||
|                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|                 subghz->protocol_result->code_last_found = key; |                 subghz->txrx->protocol_result->code_last_found = key; | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME12bit: |         case SubmenuIndexCAME12bit: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 12; |                 subghz->txrx->protocol_result->code_last_count_bit = 12; | ||||||
|                 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 |                 key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
 | ||||||
|                 subghz->protocol_result->code_last_found = key; |                 subghz->txrx->protocol_result->code_last_found = key; | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexCAME24bit: |         case SubmenuIndexCAME24bit: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 24; |                 subghz->txrx->protocol_result->code_last_count_bit = 24; | ||||||
|                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 |                 key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|                 subghz->protocol_result->code_last_found = key; |                 subghz->txrx->protocol_result->code_last_found = key; | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         // case SubmenuIndexNeroSketch:
 |         // case SubmenuIndexNeroSketch:
 | ||||||
|         //     /* code */
 |         //     /* code */
 | ||||||
|         //     break;
 |         //     break;
 | ||||||
|  |         // case SubmenuIndexNeroRadio:
 | ||||||
|  |         //     /* code */
 | ||||||
|  |         //     break;
 | ||||||
|         case SubmenuIndexGateTX: |         case SubmenuIndexGateTX: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "GateTX")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "GateTX")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 24; |                 subghz->txrx->protocol_result->code_last_count_bit = 24; | ||||||
|                 key = (key & 0x00F0FFFF) | 0xF << 16; //btn 0xF, 0xC, 0xA, 0x6
 |                 key = (key & 0x00F0FFFF) | 0xF << 16; //btn 0xF, 0xC, 0xA, 0x6
 | ||||||
|                 subghz->protocol_result->code_last_found = subghz_protocol_common_reverse_key( |                 subghz->txrx->protocol_result->code_last_found = | ||||||
|                     key, subghz->protocol_result->code_last_count_bit); |                     subghz_protocol_common_reverse_key( | ||||||
|  |                         key, subghz->txrx->protocol_result->code_last_count_bit); | ||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexDoorHan: |         case SubmenuIndexDoorHan: | ||||||
|             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "KeeLoq")) { |             if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "KeeLoq")) { | ||||||
|                 subghz->protocol_result->code_last_count_bit = 64; |                 subghz->txrx->protocol_result->code_last_count_bit = 64; | ||||||
|                 subghz->protocol_result->serial = key & 0x0FFFFFFF; |                 subghz->txrx->protocol_result->serial = key & 0x0FFFFFFF; | ||||||
|                 subghz->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
 |                 subghz->txrx->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
 | ||||||
|                 subghz->protocol_result->cnt = 0x0003; |                 subghz->txrx->protocol_result->cnt = 0x0003; | ||||||
|                 if(subghz_protocol_keeloq_set_manufacture_name( |                 if(subghz_protocol_keeloq_set_manufacture_name( | ||||||
|                        subghz->protocol_result, "DoorHan")) { |                        subghz->txrx->protocol_result, "DoorHan")) { | ||||||
|                     subghz->protocol_result->code_last_found = |                     subghz->txrx->protocol_result->code_last_found = | ||||||
|                         subghz_protocol_keeloq_gen_key(subghz->protocol_result); |                         subghz_protocol_keeloq_gen_key(subghz->txrx->protocol_result); | ||||||
|                     generated_protocol = true; |                     generated_protocol = true; | ||||||
|                 } else { |                 } else { | ||||||
|                     generated_protocol = false; |                     generated_protocol = false; | ||||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNoMan); |                     string_set(subghz->error_str, "No manufactory key"); | ||||||
|  |                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| 
 |  | ||||||
|         default: |         default: | ||||||
|             return false; |             return false; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         if(generated_protocol) { |         if(generated_protocol) { | ||||||
|             subghz->frequency = subghz_frequencies[subghz_frequencies_433_92]; |             subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||||
|             subghz->preset = FuriHalSubGhzPresetOok650Async; |             subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -2,26 +2,26 @@ | |||||||
| 
 | 
 | ||||||
| #define SCENE_NO_MAN_CUSTOM_EVENT (11UL) | #define SCENE_NO_MAN_CUSTOM_EVENT (11UL) | ||||||
| 
 | 
 | ||||||
| void subghz_scene_no_man_popup_callback(void* context) { | void subghz_scene_show_error_popup_callback(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); |     view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const void subghz_scene_no_man_on_enter(void* context) { | const void subghz_scene_show_error_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|     Popup* popup = subghz->popup; |     Popup* popup = subghz->popup; | ||||||
|     popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51); |     popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51); | ||||||
|     popup_set_header(popup, "No manufactory key", 13, 8, AlignLeft, AlignBottom); |     popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom); | ||||||
|     popup_set_timeout(popup, 1500); |     popup_set_timeout(popup, 1500); | ||||||
|     popup_set_context(popup, subghz); |     popup_set_context(popup, subghz); | ||||||
|     popup_set_callback(popup, subghz_scene_no_man_popup_callback); |     popup_set_callback(popup, subghz_scene_show_error_popup_callback); | ||||||
|     popup_enable_timeout(popup); |     popup_enable_timeout(popup); | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event) { | const bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { |         if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { | ||||||
| @ -33,7 +33,7 @@ const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event) | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const void subghz_scene_no_man_on_exit(void* context) { | const void subghz_scene_show_error_on_exit(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     // Clear view
 |     // Clear view
 | ||||||
| @ -45,4 +45,5 @@ const void subghz_scene_no_man_on_exit(void* context) { | |||||||
|     popup_set_context(popup, NULL); |     popup_set_context(popup, NULL); | ||||||
|     popup_set_timeout(popup, 0); |     popup_set_timeout(popup, 0); | ||||||
|     popup_disable_timeout(popup); |     popup_disable_timeout(popup); | ||||||
|  |     string_clean(subghz->error_str); | ||||||
| } | } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include "../views/subghz_transmitter.h" | #include "../views/subghz_transmitter.h" | ||||||
|  | #include <lib/subghz/protocols/subghz_protocol_keeloq.h> | ||||||
| 
 | 
 | ||||||
| void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) { | void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -7,42 +8,90 @@ void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* conte | |||||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); |     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void subghz_scene_transmitter_update_data_show(void* context) { | ||||||
|  |     SubGhz* subghz = context; | ||||||
|  | 
 | ||||||
|  |     if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) { | ||||||
|  |         string_t key_str; | ||||||
|  |         string_init(key_str); | ||||||
|  |         char frequency_str[10]; | ||||||
|  |         char preset_str[6]; | ||||||
|  |         uint8_t show_button = 0; | ||||||
|  |         subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, key_str); | ||||||
|  | 
 | ||||||
|  |         if((!strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) && | ||||||
|  |            (!strcmp( | ||||||
|  |                subghz_protocol_keeloq_get_manufacture_name(subghz->txrx->protocol_result), | ||||||
|  |                "Unknown"))) { | ||||||
|  |             show_button = 0; | ||||||
|  |         } else { | ||||||
|  |             show_button = 1; | ||||||
|  |         } | ||||||
|  |         snprintf( | ||||||
|  |             frequency_str, | ||||||
|  |             sizeof(frequency_str), | ||||||
|  |             "%03ld.%02ld", | ||||||
|  |             subghz->txrx->frequency / 1000000 % 1000, | ||||||
|  |             subghz->txrx->frequency / 10000 % 100); | ||||||
|  |         if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || | ||||||
|  |            subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { | ||||||
|  |             snprintf(preset_str, sizeof(preset_str), "AM"); | ||||||
|  |         } else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) { | ||||||
|  |             snprintf(preset_str, sizeof(preset_str), "FM"); | ||||||
|  |         } else { | ||||||
|  |             furi_check(0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         subghz_transmitter_add_data_to_show( | ||||||
|  |             subghz->subghz_transmitter, | ||||||
|  |             string_get_cstr(key_str), | ||||||
|  |             frequency_str, | ||||||
|  |             preset_str, | ||||||
|  |             show_button); | ||||||
|  |         string_clear(key_str); | ||||||
|  |     } else { | ||||||
|  |         string_set(subghz->error_str, "Protocol not found"); | ||||||
|  |         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const void subghz_scene_transmitter_on_enter(void* context) { | const void subghz_scene_transmitter_on_enter(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter; |     subghz_transmitter_set_callback( | ||||||
| 
 |         subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); | ||||||
|     subghz_transmitter_set_callback(subghz_transmitter, subghz_scene_transmitter_callback, subghz); |     subghz_scene_transmitter_update_data_show(subghz); | ||||||
|     subghz_transmitter_set_protocol(subghz_transmitter, subghz->protocol_result); |  | ||||||
|     subghz_transmitter_set_frequency_preset(subghz_transmitter, subghz->frequency, subghz->preset); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter); |  | ||||||
| 
 |  | ||||||
|     subghz->state_notifications = NOTIFICATION_IDLE_STATE; |     subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|  |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { | const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubghzTransmitterEventSendStart) { |         if(event.event == SubghzTransmitterEventSendStart) { | ||||||
|             subghz->state_notifications = NOTIFICATION_TX_STATE; |             subghz->state_notifications = NOTIFICATION_TX_STATE; | ||||||
|             subghz_transmitter_tx_start(subghz); |             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |                 subghz_rx_end(subghz->txrx->worker); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             } | ||||||
|  |             if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { | ||||||
|  |                 subghz_tx_start(subghz); | ||||||
|  |                 subghz_scene_transmitter_update_data_show(subghz); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateTx; | ||||||
|  |             } | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubghzTransmitterEventSendStop) { |         } else if(event.event == SubghzTransmitterEventSendStop) { | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|             subghz_transmitter_tx_stop(subghz); |             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||||
|  |                 subghz_tx_stop(subghz); | ||||||
|                 subghz_sleep(); |                 subghz_sleep(); | ||||||
|  |                 subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |             } | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubghzTransmitterEventBack) { |         } else if(event.event == SubghzTransmitterEventBack) { | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |             subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|                 subghz->scene_manager, SubGhzSceneStart); |                 subghz->scene_manager, SubGhzSceneStart); | ||||||
|             return true; |             return true; | ||||||
|         } else if(event.event == SubghzTransmitterEventNoMan) { |  | ||||||
|             subghz->state_notifications = NOTIFICATION_IDLE_STATE; |  | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |  | ||||||
|                 subghz->scene_manager, SubGhzSceneNoMan); |  | ||||||
|             return true; |  | ||||||
|         } |         } | ||||||
|     } else if(event.type == SceneManagerEventTypeTick) { |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|         if(subghz->state_notifications == NOTIFICATION_TX_STATE) { |         if(subghz->state_notifications == NOTIFICATION_TX_STATE) { | ||||||
| @ -55,7 +104,6 @@ const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent ev | |||||||
| 
 | 
 | ||||||
| const void subghz_scene_transmitter_on_exit(void* context) { | const void subghz_scene_transmitter_on_exit(void* context) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter; | 
 | ||||||
|     subghz_transmitter_set_callback(subghz_transmitter, NULL, subghz); |  | ||||||
|     subghz->state_notifications = NOTIFICATION_IDLE_STATE; |     subghz->state_notifications = NOTIFICATION_IDLE_STATE; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,22 @@ | |||||||
| #include "subghz_i.h" | #include "subghz_i.h" | ||||||
| 
 | 
 | ||||||
|  | const char* const subghz_frequencies_text[] = { | ||||||
|  |     "300.00", | ||||||
|  |     "315.00", | ||||||
|  |     "348.00", | ||||||
|  |     "387.00", | ||||||
|  |     "433.08", | ||||||
|  |     "433.92", | ||||||
|  |     "434.78", | ||||||
|  |     "438.90", | ||||||
|  |     "464.00", | ||||||
|  |     "779.00", | ||||||
|  |     "868.35", | ||||||
|  |     "915.00", | ||||||
|  |     "925.00", | ||||||
|  |     "928.00", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const uint32_t subghz_frequencies[] = { | const uint32_t subghz_frequencies[] = { | ||||||
|     /* 300 - 348 */ |     /* 300 - 348 */ | ||||||
|     300000000, |     300000000, | ||||||
| @ -20,7 +37,15 @@ const uint32_t subghz_frequencies[] = { | |||||||
|     928000000, |     928000000, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const uint32_t subghz_hopper_frequencies[] = { | ||||||
|  |     315000000, | ||||||
|  |     433920000, | ||||||
|  |     868350000, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t); | const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t); | ||||||
|  | const uint32_t subghz_hopper_frequencies_count = | ||||||
|  |     sizeof(subghz_hopper_frequencies) / sizeof(uint32_t); | ||||||
| const uint32_t subghz_frequencies_433_92 = 5; | const uint32_t subghz_frequencies_433_92 = 5; | ||||||
| 
 | 
 | ||||||
| bool subghz_custom_event_callback(void* context, uint32_t event) { | bool subghz_custom_event_callback(void* context, uint32_t event) { | ||||||
| @ -77,11 +102,6 @@ SubGhz* subghz_alloc() { | |||||||
|         SubGhzViewReceiver, |         SubGhzViewReceiver, | ||||||
|         subghz_receiver_get_view(subghz->subghz_receiver)); |         subghz_receiver_get_view(subghz->subghz_receiver)); | ||||||
| 
 | 
 | ||||||
|     // Dialog
 |  | ||||||
|     subghz->dialog_ex = dialog_ex_alloc(); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         subghz->view_dispatcher, SubGhzViewDialogEx, dialog_ex_get_view(subghz->dialog_ex)); |  | ||||||
| 
 |  | ||||||
|     // Popup
 |     // Popup
 | ||||||
|     subghz->popup = popup_alloc(); |     subghz->popup = popup_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -92,6 +112,11 @@ SubGhz* subghz_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         subghz->view_dispatcher, SubGhzViewTextInput, text_input_get_view(subghz->text_input)); |         subghz->view_dispatcher, SubGhzViewTextInput, text_input_get_view(subghz->text_input)); | ||||||
| 
 | 
 | ||||||
|  |     // Custom Widget
 | ||||||
|  |     subghz->widget = widget_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         subghz->view_dispatcher, SubGhzViewWidget, widget_get_view(subghz->widget)); | ||||||
|  | 
 | ||||||
|     // Transmitter
 |     // Transmitter
 | ||||||
|     subghz->subghz_transmitter = subghz_transmitter_alloc(); |     subghz->subghz_transmitter = subghz_transmitter_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -99,6 +124,13 @@ SubGhz* subghz_alloc() { | |||||||
|         SubGhzViewTransmitter, |         SubGhzViewTransmitter, | ||||||
|         subghz_transmitter_get_view(subghz->subghz_transmitter)); |         subghz_transmitter_get_view(subghz->subghz_transmitter)); | ||||||
| 
 | 
 | ||||||
|  |     // Variable Item List
 | ||||||
|  |     subghz->variable_item_list = variable_item_list_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         subghz->view_dispatcher, | ||||||
|  |         SubGhzViewVariableItemList, | ||||||
|  |         variable_item_list_get_view(subghz->variable_item_list)); | ||||||
|  | 
 | ||||||
|     // Carrier Test Module
 |     // Carrier Test Module
 | ||||||
|     subghz->subghz_test_carrier = subghz_test_carrier_alloc(); |     subghz->subghz_test_carrier = subghz_test_carrier_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -120,17 +152,26 @@ SubGhz* subghz_alloc() { | |||||||
|         SubGhzViewStatic, |         SubGhzViewStatic, | ||||||
|         subghz_test_static_get_view(subghz->subghz_test_static)); |         subghz_test_static_get_view(subghz->subghz_test_static)); | ||||||
| 
 | 
 | ||||||
|     //init Worker & Protocol
 |     //init Worker & Protocol & History
 | ||||||
|     subghz->worker = subghz_worker_alloc(); |     subghz->txrx = furi_alloc(sizeof(SubGhzTxRx)); | ||||||
|     subghz->protocol = subghz_protocol_alloc(); |     subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||||
|  |     subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; | ||||||
|  |     subghz->txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |     subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||||
|  |     subghz->txrx->history = subghz_history_alloc(); | ||||||
|  |     subghz->txrx->worker = subghz_worker_alloc(); | ||||||
|  |     subghz->txrx->protocol = subghz_protocol_alloc(); | ||||||
|     subghz_worker_set_overrun_callback( |     subghz_worker_set_overrun_callback( | ||||||
|         subghz->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset); |         subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset); | ||||||
|     subghz_worker_set_pair_callback( |     subghz_worker_set_pair_callback( | ||||||
|         subghz->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse); |         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse); | ||||||
|     subghz_worker_set_context(subghz->worker, subghz->protocol); |     subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->protocol); | ||||||
| 
 | 
 | ||||||
|     subghz_protocol_load_keeloq_file(subghz->protocol, "/ext/subghz/keeloq_mfcodes"); |     //Init Error_str
 | ||||||
|     subghz_protocol_load_nice_flor_s_file(subghz->protocol, "/ext/subghz/nice_floor_s_rx"); |     string_init(subghz->error_str); | ||||||
|  | 
 | ||||||
|  |     subghz_protocol_load_keeloq_file(subghz->txrx->protocol, "/ext/subghz/keeloq_mfcodes"); | ||||||
|  |     subghz_protocol_load_nice_flor_s_file(subghz->txrx->protocol, "/ext/subghz/nice_floor_s_rx"); | ||||||
| 
 | 
 | ||||||
|     //subghz_protocol_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
 |     //subghz_protocol_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
 | ||||||
| 
 | 
 | ||||||
| @ -160,18 +201,22 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTextInput); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTextInput); | ||||||
|     text_input_free(subghz->text_input); |     text_input_free(subghz->text_input); | ||||||
| 
 | 
 | ||||||
|     // Receiver
 |     // Custom Widget
 | ||||||
|  |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||||
|  |     widget_free(subghz->widget); | ||||||
|  | 
 | ||||||
|  |     // Transmitter
 | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTransmitter); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTransmitter); | ||||||
|     subghz_transmitter_free(subghz->subghz_transmitter); |     subghz_transmitter_free(subghz->subghz_transmitter); | ||||||
| 
 | 
 | ||||||
|  |     // Variable Item List
 | ||||||
|  |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewVariableItemList); | ||||||
|  |     variable_item_list_free(subghz->variable_item_list); | ||||||
|  | 
 | ||||||
|     // Submenu
 |     // Submenu
 | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); | ||||||
|     submenu_free(subghz->submenu); |     submenu_free(subghz->submenu); | ||||||
| 
 | 
 | ||||||
|     // DialogEx
 |  | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewDialogEx); |  | ||||||
|     dialog_ex_free(subghz->dialog_ex); |  | ||||||
| 
 |  | ||||||
|     // Popup
 |     // Popup
 | ||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewPopup); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewPopup); | ||||||
|     popup_free(subghz->popup); |     popup_free(subghz->popup); | ||||||
| @ -186,9 +231,14 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     subghz->gui = NULL; |     subghz->gui = NULL; | ||||||
| 
 | 
 | ||||||
|     //Worker & Protocol
 |     //Worker & Protocol & History
 | ||||||
|     subghz_protocol_free(subghz->protocol); |     subghz_protocol_free(subghz->txrx->protocol); | ||||||
|     subghz_worker_free(subghz->worker); |     subghz_worker_free(subghz->txrx->worker); | ||||||
|  |     subghz_history_free(subghz->txrx->history); | ||||||
|  |     free(subghz->txrx); | ||||||
|  | 
 | ||||||
|  |     //Error string
 | ||||||
|  |     string_clear(subghz->error_str); | ||||||
| 
 | 
 | ||||||
|     // Notifications
 |     // Notifications
 | ||||||
|     furi_record_close("notification"); |     furi_record_close("notification"); | ||||||
|  | |||||||
| @ -128,21 +128,27 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_history_add_to_history(SubGhzHistory* instance, void* context) { | bool subghz_history_add_to_history( | ||||||
|  |     SubGhzHistory* instance, | ||||||
|  |     void* context, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     FuriHalSubGhzPreset preset) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolCommon* protocol = context; |     SubGhzProtocolCommon* protocol = context; | ||||||
| 
 | 
 | ||||||
|     if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return; |     if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; | ||||||
|     if((instance->code_last_found == (protocol->code_last_found & 0xFFFF0FFFFFFFFFFF)) && |     if((instance->code_last_found == (protocol->code_last_found & 0xFFFF0FFFFFFFFFFF)) && | ||||||
|        ((millis() - instance->last_update_timestamp) < 500)) { |        ((millis() - instance->last_update_timestamp) < 500)) { | ||||||
|         instance->last_update_timestamp = millis(); |         instance->last_update_timestamp = millis(); | ||||||
|         return; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     instance->code_last_found = protocol->code_last_found & 0xFFFF0FFFFFFFFFFF; |     instance->code_last_found = protocol->code_last_found & 0xFFFF0FFFFFFFFFFF; | ||||||
|     instance->last_update_timestamp = millis(); |     instance->last_update_timestamp = millis(); | ||||||
| 
 | 
 | ||||||
|  |     instance->history[instance->last_index_write].real_frequency = frequency; | ||||||
|  |     instance->history[instance->last_index_write].preset = preset; | ||||||
|     instance->history[instance->last_index_write].te = 0; |     instance->history[instance->last_index_write].te = 0; | ||||||
|     instance->history[instance->last_index_write].manufacture_name = NULL; |     instance->history[instance->last_index_write].manufacture_name = NULL; | ||||||
|     instance->history[instance->last_index_write].name = protocol->name; |     instance->history[instance->last_index_write].name = protocol->name; | ||||||
| @ -161,4 +167,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context) { | |||||||
|     instance->history[instance->last_index_write].type_protocol = protocol->type_protocol; |     instance->history[instance->last_index_write].type_protocol = protocol->type_protocol; | ||||||
| 
 | 
 | ||||||
|     instance->last_index_write++; |     instance->last_index_write++; | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| @ -1,3 +1,4 @@ | |||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||||
| @ -94,8 +95,15 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output | |||||||
|  *  |  *  | ||||||
|  * @param instance  - SubGhzHistory instance |  * @param instance  - SubGhzHistory instance | ||||||
|  * @param context    - SubGhzProtocolCommon context |  * @param context    - SubGhzProtocolCommon context | ||||||
|  |  * @param frequency - frequency Hz | ||||||
|  |  * @param preset    - FuriHalSubGhzPreset preset | ||||||
|  |  * @return bool; | ||||||
|  */ |  */ | ||||||
| void subghz_history_add_to_history(SubGhzHistory* instance, void* context); | bool subghz_history_add_to_history( | ||||||
|  |     SubGhzHistory* instance, | ||||||
|  |     void* context, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     FuriHalSubGhzPreset preset); | ||||||
| 
 | 
 | ||||||
| /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
 | ||||||
|  *  |  *  | ||||||
| @ -104,3 +112,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context); | |||||||
|  * @return SubGhzProtocolCommonLoad* |  * @return SubGhzProtocolCommonLoad* | ||||||
|  */ |  */ | ||||||
| SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); | SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); | ||||||
|  | 
 | ||||||
|  | void subghz_hopper_update(void* context); | ||||||
|  | |||||||
| @ -19,6 +19,9 @@ void subghz_begin(FuriHalSubGhzPreset preset) { | |||||||
| 
 | 
 | ||||||
| uint32_t subghz_rx(void* context, uint32_t frequency) { | uint32_t subghz_rx(void* context, uint32_t frequency) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|  |     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||||
|  |         furi_check(0); | ||||||
|  |     } | ||||||
|     SubGhzWorker* worker = context; |     SubGhzWorker* worker = context; | ||||||
| 
 | 
 | ||||||
|     furi_hal_subghz_idle(); |     furi_hal_subghz_idle(); | ||||||
| @ -33,6 +36,9 @@ uint32_t subghz_rx(void* context, uint32_t frequency) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_tx(uint32_t frequency) { | uint32_t subghz_tx(uint32_t frequency) { | ||||||
|  |     if(!furi_hal_subghz_is_frequency_valid(frequency)) { | ||||||
|  |         furi_check(0); | ||||||
|  |     } | ||||||
|     furi_hal_subghz_idle(); |     furi_hal_subghz_idle(); | ||||||
|     uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); |     uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||||
| @ -53,6 +59,7 @@ void subghz_rx_end(void* context) { | |||||||
|         subghz_worker_stop(worker); |         subghz_worker_stop(worker); | ||||||
|         furi_hal_subghz_stop_async_rx(); |         furi_hal_subghz_stop_async_rx(); | ||||||
|     } |     } | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_sleep(void) { | void subghz_sleep(void) { | ||||||
| @ -66,41 +73,46 @@ void subghz_frequency_preset_to_str(void* context, string_t output) { | |||||||
|         output, |         output, | ||||||
|         "Frequency: %d\n" |         "Frequency: %d\n" | ||||||
|         "Preset: %d\n", |         "Preset: %d\n", | ||||||
|         (int)subghz->frequency, |         (int)subghz->txrx->frequency, | ||||||
|         (int)subghz->preset); |         (int)subghz->txrx->preset); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_tx_start(void* context) { | void subghz_tx_start(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     subghz->encoder = subghz_protocol_encoder_common_alloc(); |     subghz->txrx->encoder = subghz_protocol_encoder_common_alloc(); | ||||||
|     subghz->encoder->repeat = 200; //max repeat with the button held down
 |     subghz->txrx->encoder->repeat = 200; //max repeat with the button held down
 | ||||||
|     //get upload
 |     //get upload
 | ||||||
|     if(subghz->protocol_result->get_upload_protocol) { |     if(subghz->txrx->protocol_result->get_upload_protocol) { | ||||||
|         if(subghz->protocol_result->get_upload_protocol(subghz->protocol_result, subghz->encoder)) { |         if(subghz->txrx->protocol_result->get_upload_protocol( | ||||||
|             if(subghz->preset) { |                subghz->txrx->protocol_result, subghz->txrx->encoder)) { | ||||||
|                 subghz_begin(subghz->preset); |             if(subghz->txrx->preset) { | ||||||
|  |                 subghz_begin(subghz->txrx->preset); | ||||||
|             } else { |             } else { | ||||||
|                 subghz_begin(FuriHalSubGhzPresetOok650Async); |                 subghz_begin(FuriHalSubGhzPresetOok270Async); | ||||||
|             } |             } | ||||||
|             if(subghz->frequency) { |             if(subghz->txrx->frequency) { | ||||||
|                 subghz_tx(subghz->frequency); |                 subghz_tx(subghz->txrx->frequency); | ||||||
|             } else { |             } else { | ||||||
|                 subghz_tx(433920000); |                 subghz_tx(433920000); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             //Start TX
 |             //Start TX
 | ||||||
|             furi_hal_subghz_start_async_tx(subghz_protocol_encoder_common_yield, subghz->encoder); |             furi_hal_subghz_start_async_tx( | ||||||
|  |                 subghz_protocol_encoder_common_yield, subghz->txrx->encoder); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_tx_stop(void* context) { | void subghz_tx_stop(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     //Stop TX
 |     //Stop TX
 | ||||||
|     furi_hal_subghz_stop_async_tx(); |     furi_hal_subghz_stop_async_tx(); | ||||||
|     subghz_protocol_encoder_common_free(subghz->encoder); |     subghz_protocol_encoder_common_free(subghz->txrx->encoder); | ||||||
|  |     furi_hal_subghz_idle(); | ||||||
|     //if protocol dynamic then we save the last upload
 |     //if protocol dynamic then we save the last upload
 | ||||||
|     if(subghz->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) { |     if(subghz->txrx->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) { | ||||||
|         subghz_save_protocol_to_file(subghz, subghz->text_store); |         subghz_save_protocol_to_file(subghz, subghz->text_store); | ||||||
|     } |     } | ||||||
|     notification_message(subghz->notifications, &sequence_reset_red); |     notification_message(subghz->notifications, &sequence_reset_red); | ||||||
| @ -133,7 +145,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|         if(res != 1) { |         if(res != 1) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         subghz->frequency = (uint32_t)data; |         subghz->txrx->frequency = (uint32_t)data; | ||||||
| 
 | 
 | ||||||
|         // Read and parse preset from 2st line
 |         // Read and parse preset from 2st line
 | ||||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { |         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||||
| @ -143,7 +155,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|         if(res != 1) { |         if(res != 1) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         subghz->preset = (FuriHalSubGhzPreset)data; |         subghz->txrx->preset = (FuriHalSubGhzPreset)data; | ||||||
| 
 | 
 | ||||||
|         // Read and parse name protocol from 2st line
 |         // Read and parse name protocol from 2st line
 | ||||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { |         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||||
| @ -151,13 +163,13 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|         } |         } | ||||||
|         // strlen("Protocol: ") = 10
 |         // strlen("Protocol: ") = 10
 | ||||||
|         string_right(temp_str, 10); |         string_right(temp_str, 10); | ||||||
|         subghz->protocol_result = |         subghz->txrx->protocol_result = | ||||||
|             subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str)); |             subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str)); | ||||||
|         if(subghz->protocol_result == NULL) { |         if(subghz->txrx->protocol_result == NULL) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(!subghz->protocol_result->to_load_protocol_from_file( |         if(!subghz->txrx->protocol_result->to_load_protocol_from_file( | ||||||
|                file_worker, subghz->protocol_result)) { |                file_worker, subghz->txrx->protocol_result)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         loaded = true; |         loaded = true; | ||||||
| @ -175,8 +187,9 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | ||||||
|  |     furi_assert(context); | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     furi_assert(subghz->protocol_result); |     furi_assert(subghz->txrx->protocol_result); | ||||||
|     FileWorker* file_worker = file_worker_alloc(false); |     FileWorker* file_worker = file_worker_alloc(false); | ||||||
|     string_t dev_file_name; |     string_t dev_file_name; | ||||||
|     string_init(dev_file_name); |     string_init(dev_file_name); | ||||||
| @ -210,7 +223,7 @@ bool subghz_save_protocol_to_file(void* context, const char* dev_name) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         //Get string save
 |         //Get string save
 | ||||||
|         subghz->protocol_result->to_save_string(subghz->protocol_result, temp_str); |         subghz->txrx->protocol_result->to_save_string(subghz->txrx->protocol_result, temp_str); | ||||||
|         // Prepare and write data to file
 |         // Prepare and write data to file
 | ||||||
|         if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_size(temp_str))) { |         if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_size(temp_str))) { | ||||||
|             break; |             break; | ||||||
| @ -276,7 +289,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { | |||||||
|         if(sscanf_res != 1) { |         if(sscanf_res != 1) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         subghz->frequency = (uint32_t)data; |         subghz->txrx->frequency = (uint32_t)data; | ||||||
| 
 | 
 | ||||||
|         // Read and parse preset from 2st line
 |         // Read and parse preset from 2st line
 | ||||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { |         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||||
| @ -286,7 +299,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { | |||||||
|         if(sscanf_res != 1) { |         if(sscanf_res != 1) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         subghz->preset = (FuriHalSubGhzPreset)data; |         subghz->txrx->preset = (FuriHalSubGhzPreset)data; | ||||||
| 
 | 
 | ||||||
|         // Read and parse name protocol from 3st line
 |         // Read and parse name protocol from 3st line
 | ||||||
|         if(!file_worker_read_until(file_worker, temp_str, '\n')) { |         if(!file_worker_read_until(file_worker, temp_str, '\n')) { | ||||||
| @ -294,13 +307,13 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { | |||||||
|         } |         } | ||||||
|         // strlen("Protocol: ") = 10
 |         // strlen("Protocol: ") = 10
 | ||||||
|         string_right(temp_str, 10); |         string_right(temp_str, 10); | ||||||
|         subghz->protocol_result = |         subghz->txrx->protocol_result = | ||||||
|             subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str)); |             subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str)); | ||||||
|         if(subghz->protocol_result == NULL) { |         if(subghz->txrx->protocol_result == NULL) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(!subghz->protocol_result->to_load_protocol_from_file( |         if(!subghz->txrx->protocol_result->to_load_protocol_from_file( | ||||||
|                file_worker, subghz->protocol_result)) { |                file_worker, subghz->txrx->protocol_result)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         res = true; |         res = true; | ||||||
| @ -328,3 +341,57 @@ uint32_t subghz_random_serial(void) { | |||||||
|     } |     } | ||||||
|     return (uint32_t)rand(); |     return (uint32_t)rand(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void subghz_hopper_update(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhzTxRx* txrx = context; | ||||||
|  | 
 | ||||||
|  |     switch(txrx->hopper_state) { | ||||||
|  |     case SubGhzHopperStateOFF: | ||||||
|  |         return; | ||||||
|  |         break; | ||||||
|  |     case SubGhzHopperStatePause: | ||||||
|  |         return; | ||||||
|  |         break; | ||||||
|  |     case SubGhzHopperStateRSSITimeOut: | ||||||
|  |         if(txrx->hopper_timeout != 0) { | ||||||
|  |             txrx->hopper_timeout--; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     float rssi = -127.0f; | ||||||
|  |     if(txrx->hopper_state != SubGhzHopperStateRSSITimeOut) { | ||||||
|  |         // See RSSI Calculation timings in CC1101 17.3 RSSI
 | ||||||
|  |         rssi = furi_hal_subghz_get_rssi(); | ||||||
|  | 
 | ||||||
|  |         // Stay if RSSI is high enough
 | ||||||
|  |         if(rssi > -90.0f) { | ||||||
|  |             txrx->hopper_timeout = 10; | ||||||
|  |             txrx->hopper_state = SubGhzHopperStateRSSITimeOut; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         txrx->hopper_state = SubGhzHopperStateRunnig; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Select next frequency
 | ||||||
|  |     if(txrx->hopper_idx_frequency < subghz_hopper_frequencies_count - 1) { | ||||||
|  |         txrx->hopper_idx_frequency++; | ||||||
|  |     } else { | ||||||
|  |         txrx->hopper_idx_frequency = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(txrx->txrx_state == SubGhzTxRxStateRx) { | ||||||
|  |         subghz_rx_end(txrx->worker); | ||||||
|  |         txrx->txrx_state = SubGhzTxRxStateIdle; | ||||||
|  |     }; | ||||||
|  |     if(txrx->txrx_state == SubGhzTxRxStateIdle) { | ||||||
|  |         subghz_protocol_reset(txrx->protocol); | ||||||
|  |         txrx->frequency = subghz_hopper_frequencies[txrx->hopper_idx_frequency]; | ||||||
|  |         subghz_rx(txrx->worker, txrx->frequency); | ||||||
|  |         txrx->txrx_state = SubGhzTxRxStateRx; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -15,9 +15,9 @@ | |||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
| #include <gui/modules/text_input.h> | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
| 
 | 
 | ||||||
| #include <subghz/scenes/subghz_scene.h> | #include <subghz/scenes/subghz_scene.h> | ||||||
| 
 | 
 | ||||||
| @ -26,6 +26,8 @@ | |||||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> | #include <lib/subghz/protocols/subghz_protocol_common.h> | ||||||
| #include "subghz_history.h" | #include "subghz_history.h" | ||||||
| 
 | 
 | ||||||
|  | #include <gui/modules/variable-item-list.h> | ||||||
|  | 
 | ||||||
| #define SUBGHZ_TEXT_STORE_SIZE 128 | #define SUBGHZ_TEXT_STORE_SIZE 128 | ||||||
| 
 | 
 | ||||||
| #define NOTIFICATION_STARTING_STATE 0u | #define NOTIFICATION_STARTING_STATE 0u | ||||||
| @ -33,48 +35,81 @@ | |||||||
| #define NOTIFICATION_TX_STATE 2u | #define NOTIFICATION_TX_STATE 2u | ||||||
| #define NOTIFICATION_RX_STATE 3u | #define NOTIFICATION_RX_STATE 3u | ||||||
| 
 | 
 | ||||||
|  | extern const char* const subghz_frequencies_text[]; | ||||||
| extern const uint32_t subghz_frequencies[]; | extern const uint32_t subghz_frequencies[]; | ||||||
|  | extern const uint32_t subghz_hopper_frequencies[]; | ||||||
| extern const uint32_t subghz_frequencies_count; | extern const uint32_t subghz_frequencies_count; | ||||||
|  | extern const uint32_t subghz_hopper_frequencies_count; | ||||||
| extern const uint32_t subghz_frequencies_433_92; | extern const uint32_t subghz_frequencies_433_92; | ||||||
| 
 | 
 | ||||||
| struct SubGhz { | /** SubGhzTxRx state */ | ||||||
|     Gui* gui; | typedef enum { | ||||||
|     NotificationApp* notifications; |     SubGhzTxRxStateIdle, | ||||||
|  |     SubGhzTxRxStateRx, | ||||||
|  |     SubGhzTxRxStateTx, | ||||||
|  | } SubGhzTxRxState; | ||||||
| 
 | 
 | ||||||
|  | /** SubGhzHopperState state */ | ||||||
|  | typedef enum { | ||||||
|  |     SubGhzHopperStateOFF, | ||||||
|  |     SubGhzHopperStateRunnig, | ||||||
|  |     SubGhzHopperStatePause, | ||||||
|  |     SubGhzHopperStateRSSITimeOut, | ||||||
|  | } SubGhzHopperState; | ||||||
|  | 
 | ||||||
|  | struct SubGhzTxRx { | ||||||
|     SubGhzWorker* worker; |     SubGhzWorker* worker; | ||||||
|     SubGhzProtocol* protocol; |     SubGhzProtocol* protocol; | ||||||
|     SubGhzProtocolCommon* protocol_result; |     SubGhzProtocolCommon* protocol_result; | ||||||
|     SubGhzProtocolCommonEncoder* encoder; |     SubGhzProtocolCommonEncoder* encoder; | ||||||
|     uint32_t frequency; |     uint32_t frequency; | ||||||
|     FuriHalSubGhzPreset preset; |     FuriHalSubGhzPreset preset; | ||||||
|  |     SubGhzHistory* history; | ||||||
|  |     uint16_t idx_menu_chosen; | ||||||
|  |     SubGhzTxRxState txrx_state; | ||||||
|  |     //bool hopper_runing;
 | ||||||
|  |     SubGhzHopperState hopper_state; | ||||||
|  |     uint8_t hopper_timeout; | ||||||
|  |     uint8_t hopper_idx_frequency; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct SubGhzTxRx SubGhzTxRx; | ||||||
|  | 
 | ||||||
|  | struct SubGhz { | ||||||
|  |     Gui* gui; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  | 
 | ||||||
|  |     SubGhzTxRx* txrx; | ||||||
| 
 | 
 | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
| 
 | 
 | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     DialogEx* dialog_ex; |  | ||||||
|     Popup* popup; |     Popup* popup; | ||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|  |     Widget* widget; | ||||||
|     char text_store[SUBGHZ_TEXT_STORE_SIZE + 1]; |     char text_store[SUBGHZ_TEXT_STORE_SIZE + 1]; | ||||||
|     uint8_t state_notifications; |     uint8_t state_notifications; | ||||||
| 
 | 
 | ||||||
|     SubghzReceiver* subghz_receiver; |     SubghzReceiver* subghz_receiver; | ||||||
|     SubghzTransmitter* subghz_transmitter; |     SubghzTransmitter* subghz_transmitter; | ||||||
|  |     VariableItemList* variable_item_list; | ||||||
| 
 | 
 | ||||||
|     SubghzTestStatic* subghz_test_static; |     SubghzTestStatic* subghz_test_static; | ||||||
|     SubghzTestCarrier* subghz_test_carrier; |     SubghzTestCarrier* subghz_test_carrier; | ||||||
|     SubghzTestPacket* subghz_test_packet; |     SubghzTestPacket* subghz_test_packet; | ||||||
|  |     string_t error_str; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubGhzViewMenu, |     SubGhzViewMenu, | ||||||
| 
 | 
 | ||||||
|     SubGhzViewDialogEx, |  | ||||||
|     SubGhzViewReceiver, |     SubGhzViewReceiver, | ||||||
|     SubGhzViewPopup, |     SubGhzViewPopup, | ||||||
|     SubGhzViewTextInput, |     SubGhzViewTextInput, | ||||||
|  |     SubGhzViewWidget, | ||||||
|     SubGhzViewTransmitter, |     SubGhzViewTransmitter, | ||||||
| 
 |     SubGhzViewVariableItemList, | ||||||
|     SubGhzViewStatic, |     SubGhzViewStatic, | ||||||
|     SubGhzViewTestCarrier, |     SubGhzViewTestCarrier, | ||||||
|     SubGhzViewTestPacket, |     SubGhzViewTestPacket, | ||||||
| @ -86,8 +121,8 @@ uint32_t subghz_tx(uint32_t frequency); | |||||||
| void subghz_idle(void); | void subghz_idle(void); | ||||||
| void subghz_rx_end(void* context); | void subghz_rx_end(void* context); | ||||||
| void subghz_sleep(void); | void subghz_sleep(void); | ||||||
| void subghz_transmitter_tx_start(void* context); | void subghz_tx_start(void* context); | ||||||
| void subghz_transmitter_tx_stop(void* context); | void subghz_tx_stop(void* context); | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path); | bool subghz_key_load(SubGhz* subghz, const char* file_path); | ||||||
| bool subghz_save_protocol_to_file(void* context, const char* dev_name); | bool subghz_save_protocol_to_file(void* context, const char* dev_name); | ||||||
| bool subghz_load_protocol_from_file(SubGhz* subghz); | bool subghz_load_protocol_from_file(SubGhz* subghz); | ||||||
|  | |||||||
| @ -1,42 +1,32 @@ | |||||||
| #include "subghz_receiver.h" | #include "subghz_receiver.h" | ||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <furi.h> | 
 | ||||||
| #include <furi-hal.h> |  | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <notification/notification-messages.h> |  | ||||||
| #include <lib/subghz/protocols/subghz_protocol_princeton.h> |  | ||||||
| 
 |  | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <m-array.h> | ||||||
| 
 | 
 | ||||||
| #define FRAME_HEIGHT 12 | #define FRAME_HEIGHT 12 | ||||||
| #define MAX_LEN_PX 100 | #define MAX_LEN_PX 100 | ||||||
| #define MENU_ITEMS 4 | #define MENU_ITEMS 4 | ||||||
| 
 | 
 | ||||||
| #define COUNT_FREQUNCY_HOPPER 3 | typedef struct { | ||||||
| const uint32_t subghz_frequencies_hopper[] = { |     string_t item_str; | ||||||
|     /* 300 - 348 */ |     uint8_t type; | ||||||
|     315000000, | } SubGhzReceiverMenuItem; | ||||||
|     /* 387 - 464 */ | 
 | ||||||
|     433920000, /* LPD433 mid */ | ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) | ||||||
|     /* 779 - 928 */ | 
 | ||||||
|     868350000, | #define M_OPL_SubGhzReceiverMenuItemArray_t() \ | ||||||
|  |     ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST) | ||||||
|  | 
 | ||||||
|  | struct SubGhzReceiverHistory { | ||||||
|  |     SubGhzReceiverMenuItemArray_t data; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef struct SubGhzReceiverHistory SubGhzReceiverHistory; | ||||||
|     ReceiverSceneStart, |  | ||||||
|     ReceiverSceneMain, |  | ||||||
|     ReceiverSceneConfig, |  | ||||||
|     ReceiverSceneInfo, |  | ||||||
| } SubghzReceiverScene; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     SubGhzHopperStateOFF, |  | ||||||
|     SubGhzHopperStatePause, |  | ||||||
|     SubGhzHopperStateRunnig, |  | ||||||
|     SubGhzHopperStateRSSITimeOut, |  | ||||||
| } SubGhzHopperState; |  | ||||||
| 
 | 
 | ||||||
| static const Icon* ReceiverItemIcons[] = { | static const Icon* ReceiverItemIcons[] = { | ||||||
|     [TYPE_PROTOCOL_UNKNOWN] = &I_Quest_7x8, |     [TYPE_PROTOCOL_UNKNOWN] = &I_Quest_7x8, | ||||||
| @ -48,27 +38,16 @@ struct SubghzReceiver { | |||||||
|     View* view; |     View* view; | ||||||
|     SubghzReceiverCallback callback; |     SubghzReceiverCallback callback; | ||||||
|     void* context; |     void* context; | ||||||
|     SubGhzWorker* worker; |  | ||||||
|     SubGhzProtocol* protocol; |  | ||||||
|     osTimerId timer; |  | ||||||
|     SubGhzHopperState hopper_state; |  | ||||||
|     uint8_t hopper_timeout; |  | ||||||
|     uint32_t event_key_sequence; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     string_t text; |     string_t frequency_str; | ||||||
|     uint16_t scene; |     string_t preset_str; | ||||||
|     SubGhzProtocolCommon* protocol_result; |     string_t history_stat_str; | ||||||
|     SubGhzHistory* history; |     SubGhzReceiverHistory* history; | ||||||
|     uint8_t frequency; |  | ||||||
|     uint8_t temp_frequency; |  | ||||||
|     uint32_t real_frequency; |  | ||||||
| 
 |  | ||||||
|     uint16_t idx; |     uint16_t idx; | ||||||
|     uint16_t list_offset; |     uint16_t list_offset; | ||||||
|     uint16_t history_item; |     uint16_t history_item; | ||||||
|     bool menu; |  | ||||||
| } SubghzReceiverModel; | } SubghzReceiverModel; | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_set_callback( | void subghz_receiver_set_callback( | ||||||
| @ -81,35 +60,6 @@ void subghz_receiver_set_callback( | |||||||
|     subghz_receiver->context = context; |     subghz_receiver->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_set_protocol( |  | ||||||
|     SubghzReceiver* subghz_receiver, |  | ||||||
|     SubGhzProtocolCommon* protocol_result, |  | ||||||
|     SubGhzProtocol* protocol) { |  | ||||||
|     furi_assert(subghz_receiver); |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             model->protocol_result = protocol_result; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
|     subghz_receiver->protocol = protocol; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver) { |  | ||||||
|     furi_assert(subghz_receiver); |  | ||||||
|     SubGhzProtocolCommon* result = NULL; |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             result = model->protocol_result; |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker) { |  | ||||||
|     furi_assert(subghz_receiver); |  | ||||||
|     subghz_receiver->worker = worker; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) { | static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) { | ||||||
|     furi_assert(subghz_receiver); |     furi_assert(subghz_receiver); | ||||||
| 
 | 
 | ||||||
| @ -129,6 +79,38 @@ static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void subghz_receiver_add_item_to_menu( | ||||||
|  |     SubghzReceiver* subghz_receiver, | ||||||
|  |     const char* name, | ||||||
|  |     uint8_t type) { | ||||||
|  |     furi_assert(subghz_receiver); | ||||||
|  |     with_view_model( | ||||||
|  |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|  |             SubGhzReceiverMenuItem* item_menu = | ||||||
|  |                 SubGhzReceiverMenuItemArray_push_raw(model->history->data); | ||||||
|  |             string_init_set_str(item_menu->item_str, name); | ||||||
|  |             item_menu->type = type; | ||||||
|  |             model->history_item++; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     subghz_receiver_update_offset(subghz_receiver); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_receiver_add_data_statusbar( | ||||||
|  |     SubghzReceiver* subghz_receiver, | ||||||
|  |     const char* frequency_str, | ||||||
|  |     const char* preset_str, | ||||||
|  |     const char* history_stat_str) { | ||||||
|  |     furi_assert(subghz_receiver); | ||||||
|  |     with_view_model( | ||||||
|  |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|  |             string_set(model->frequency_str, frequency_str); | ||||||
|  |             string_set(model->preset_str, preset_str); | ||||||
|  |             string_set(model->history_stat_str, history_stat_str); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); |     canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); | ||||||
| @ -144,163 +126,56 @@ static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scroll | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_draw(Canvas* canvas, SubghzReceiverModel* model) { | void subghz_receiver_draw(Canvas* canvas, SubghzReceiverModel* model) { | ||||||
|     bool scrollbar = model->history_item > 4; |  | ||||||
|     string_t str_buff; |  | ||||||
|     char buffer[64]; |  | ||||||
|     uint32_t frequency; |  | ||||||
|     string_init(str_buff); |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  | 
 | ||||||
|  |     elements_button_left(canvas, "Config"); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str)); | ||||||
|  |     canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str)); | ||||||
|  |     canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str)); | ||||||
|  |     if(model->history_item == 0) { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         canvas_draw_str(canvas, 63, 46, "Scanning..."); | ||||||
|  |         canvas_draw_line(canvas, 46, 51, 125, 51); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     canvas_draw_line(canvas, 46, 51, 125, 51); | ||||||
|  | 
 | ||||||
|  |     bool scrollbar = model->history_item > 4; | ||||||
|  |     string_t str_buff; | ||||||
|  |     string_init(str_buff); | ||||||
|  | 
 | ||||||
|  |     SubGhzReceiverMenuItem* item_menu; | ||||||
| 
 | 
 | ||||||
|     switch(model->scene) { |  | ||||||
|     case ReceiverSceneMain: |  | ||||||
|     for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { |     for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { | ||||||
|         size_t idx = CLAMP(i + model->list_offset, model->history_item, 0); |         size_t idx = CLAMP(i + model->list_offset, model->history_item, 0); | ||||||
|             subghz_history_get_text_item_menu(model->history, str_buff, idx); |         item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); | ||||||
|  |         string_set(str_buff, item_menu->item_str); | ||||||
|         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); |         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); | ||||||
|         if(model->idx == idx) { |         if(model->idx == idx) { | ||||||
|             subghz_receiver_draw_frame(canvas, i, scrollbar); |             subghz_receiver_draw_frame(canvas, i, scrollbar); | ||||||
|         } else { |         } else { | ||||||
|             canvas_set_color(canvas, ColorBlack); |             canvas_set_color(canvas, ColorBlack); | ||||||
|         } |         } | ||||||
|             canvas_draw_icon( |         canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); | ||||||
|                 canvas, |  | ||||||
|                 1, |  | ||||||
|                 2 + i * FRAME_HEIGHT, |  | ||||||
|                 ReceiverItemIcons[subghz_history_get_type_protocol(model->history, idx)]); |  | ||||||
|         canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); |         canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||||
|         string_clean(str_buff); |         string_clean(str_buff); | ||||||
|     } |     } | ||||||
|     if(scrollbar) { |     if(scrollbar) { | ||||||
|         elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); |         elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); | ||||||
|     } |     } | ||||||
|         canvas_set_color(canvas, ColorBlack); |  | ||||||
|         canvas_set_font(canvas, FontSecondary); |  | ||||||
| 
 |  | ||||||
|         elements_button_left(canvas, "Config"); |  | ||||||
|         canvas_draw_line(canvas, 46, 51, 125, 51); |  | ||||||
|         if(subghz_history_get_text_space_left(model->history, str_buff)) { |  | ||||||
|             canvas_draw_str(canvas, 54, 62, string_get_cstr(str_buff)); |  | ||||||
|         } else { |  | ||||||
|             if((model->real_frequency / 1000 % 10) > 4) { |  | ||||||
|                 frequency = model->real_frequency + 10000; |  | ||||||
|             } else { |  | ||||||
|                 frequency = model->real_frequency; |  | ||||||
|             } |  | ||||||
|             snprintf( |  | ||||||
|                 buffer, |  | ||||||
|                 sizeof(buffer), |  | ||||||
|                 "%03ld.%02ld", |  | ||||||
|                 frequency / 1000000 % 1000, |  | ||||||
|                 frequency / 10000 % 100); |  | ||||||
|             canvas_draw_str(canvas, 44, 62, buffer); |  | ||||||
|             canvas_draw_str(canvas, 79, 62, "AM"); |  | ||||||
|             canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff)); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneStart: |  | ||||||
|         canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); |  | ||||||
|         canvas_set_font(canvas, FontPrimary); |  | ||||||
|         canvas_draw_str(canvas, 63, 46, "Scanning..."); |  | ||||||
|         canvas_set_color(canvas, ColorBlack); |  | ||||||
|         canvas_set_font(canvas, FontSecondary); |  | ||||||
|         elements_button_left(canvas, "Config"); |  | ||||||
|         if((model->real_frequency / 1000 % 10) > 4) { |  | ||||||
|             frequency = model->real_frequency + 10000; |  | ||||||
|         } else { |  | ||||||
|             frequency = model->real_frequency; |  | ||||||
|         } |  | ||||||
|         snprintf( |  | ||||||
|             buffer, |  | ||||||
|             sizeof(buffer), |  | ||||||
|             "%03ld.%02ld", |  | ||||||
|             frequency / 1000000 % 1000, |  | ||||||
|             frequency / 10000 % 100); |  | ||||||
|         canvas_draw_str(canvas, 44, 62, buffer); |  | ||||||
|         canvas_draw_str(canvas, 79, 62, "AM"); |  | ||||||
|         subghz_history_get_text_space_left(model->history, str_buff); |  | ||||||
|         canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff)); |  | ||||||
|         canvas_draw_line(canvas, 46, 51, 125, 51); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneConfig: |  | ||||||
|         if(model->frequency < subghz_frequencies_count) { |  | ||||||
|             snprintf( |  | ||||||
|                 buffer, |  | ||||||
|                 sizeof(buffer), |  | ||||||
|                 "Frequency:  < %03ld.%03ldMHz >", |  | ||||||
|                 model->real_frequency / 1000000 % 1000, |  | ||||||
|                 model->real_frequency / 1000 % 1000); |  | ||||||
|             canvas_draw_str(canvas, 0, 8, buffer); |  | ||||||
|             canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <OFF>"); |  | ||||||
|         } else { |  | ||||||
|             canvas_draw_str(canvas, 0, 8, "Frequency: < --- >"); |  | ||||||
|             canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <ON>"); |  | ||||||
|         } |  | ||||||
|         canvas_draw_str(canvas, 0, 28, "Modulation: <AM>"); |  | ||||||
| 
 |  | ||||||
|         elements_button_center(canvas, "Save"); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneInfo: |  | ||||||
|         canvas_set_font(canvas, FontSecondary); |  | ||||||
|         elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text)); |  | ||||||
|         snprintf( |  | ||||||
|             buffer, |  | ||||||
|             sizeof(buffer), |  | ||||||
|             "%03ld.%03ld", |  | ||||||
|             subghz_history_get_frequency(model->history, model->idx) / 1000000 % 1000, |  | ||||||
|             subghz_history_get_frequency(model->history, model->idx) / 1000 % 1000); |  | ||||||
|         canvas_draw_str(canvas, 90, 8, buffer); |  | ||||||
|         if(model->protocol_result && model->protocol_result->to_save_string && |  | ||||||
|            strcmp(model->protocol_result->name, "KeeLoq")) { |  | ||||||
|             elements_button_right(canvas, "Save"); |  | ||||||
|             elements_button_center(canvas, "Send"); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     default: |  | ||||||
| 
 |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(str_buff); |     string_clear(str_buff); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_history_full(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubghzReceiver* subghz_receiver = context; |  | ||||||
|     subghz_receiver->callback(SubghzReceverEventSendHistoryFull, subghz_receiver->context); |  | ||||||
|     subghz_receiver->hopper_state = SubGhzHopperStateOFF; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool subghz_receiver_input(InputEvent* event, void* context) { | bool subghz_receiver_input(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |  | ||||||
|     uint8_t scene = 0; |  | ||||||
|     SubghzReceiver* subghz_receiver = context; |     SubghzReceiver* subghz_receiver = context; | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             scene = model->scene; |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
| 
 | 
 | ||||||
|     bool can_be_saved = false; |  | ||||||
| 
 |  | ||||||
|     switch(scene) { |  | ||||||
|     case ReceiverSceneMain: |  | ||||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { |     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||||
|             with_view_model( |         subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context); | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     model->idx = 0; |  | ||||||
|                     model->list_offset = 0; |  | ||||||
|                     model->history_item = 0; |  | ||||||
|                     subghz_history_clean(model->history); |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|             return false; |  | ||||||
|     } else if( |     } else if( | ||||||
|         event->key == InputKeyUp && |         event->key == InputKeyUp && | ||||||
|         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { |         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
| @ -314,292 +189,50 @@ bool subghz_receiver_input(InputEvent* event, void* context) { | |||||||
|         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { |         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { |             subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|                     if(model->idx != subghz_history_get_item(model->history) - 1) model->idx++; |                 if(model->idx != model->history_item - 1) model->idx++; | ||||||
|                 return true; |                 return true; | ||||||
|             }); |             }); | ||||||
|     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { |     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     model->scene = ReceiverSceneConfig; |  | ||||||
|                     model->temp_frequency = model->frequency; |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|         subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); |         subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); | ||||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { |     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||||
|             subghz_receiver->event_key_sequence = event->sequence; |  | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { |             subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|                     string_clean(model->text); |                 if(model->history_item != 0) { | ||||||
|                     model->protocol_result = subghz_protocol_get_by_name( |                     subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context); | ||||||
|                         subghz_receiver->protocol, |  | ||||||
|                         subghz_history_get_name(model->history, model->idx)); |  | ||||||
|                     if(model->protocol_result->to_load_protocol != NULL) { |  | ||||||
|                         model->protocol_result->to_load_protocol( |  | ||||||
|                             model->protocol_result, |  | ||||||
|                             subghz_history_get_raw_data(model->history, model->idx)); |  | ||||||
|                         model->protocol_result->to_string(model->protocol_result, model->text); |  | ||||||
|                         model->scene = ReceiverSceneInfo; |  | ||||||
|                 } |                 } | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneInfo: |  | ||||||
|         with_view_model( |  | ||||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                 can_be_saved = |  | ||||||
|                     (model->protocol_result && model->protocol_result->to_save_string && |  | ||||||
|                      strcmp(model->protocol_result->name, "KeeLoq")); |  | ||||||
|                 return false; |                 return false; | ||||||
|             }); |             }); | ||||||
|         if(event->key == InputKeyBack && event->type == InputTypeShort) { |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     subghz_rx_end(subghz_receiver->worker); |  | ||||||
|                     model->real_frequency = |  | ||||||
|                         subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]); |  | ||||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|                     model->scene = ReceiverSceneMain; |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); |  | ||||||
|         } else if(can_be_saved && event->key == InputKeyRight) { |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventSave, subghz_receiver->context); |  | ||||||
|             return false; |  | ||||||
|         } else if( |  | ||||||
|             can_be_saved && event->key == InputKeyOk && event->type == InputTypePress && |  | ||||||
|             subghz_receiver->event_key_sequence != event->sequence) { |  | ||||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; |  | ||||||
|             subghz_rx_end(subghz_receiver->worker); |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventSendStart, subghz_receiver->context); |  | ||||||
|             return true; |  | ||||||
|         } else if( |  | ||||||
|             can_be_saved && event->key == InputKeyOk && event->type == InputTypeRelease && |  | ||||||
|             subghz_receiver->event_key_sequence != event->sequence) { |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventSendStop, subghz_receiver->context); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneConfig: |  | ||||||
|         if(event->type != InputTypeShort) return false; |  | ||||||
|         if(event->key == InputKeyBack) { |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     model->frequency = model->temp_frequency; |  | ||||||
|                     model->real_frequency = subghz_frequencies[model->frequency]; |  | ||||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|                     if(subghz_history_get_item(model->history) == 0) { |  | ||||||
|                         model->scene = ReceiverSceneStart; |  | ||||||
|                     } else { |  | ||||||
|                         model->scene = ReceiverSceneMain; |  | ||||||
|                     } |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); |  | ||||||
|         } else if(event->key == InputKeyOk) { |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     if(model->frequency < subghz_frequencies_count) { |  | ||||||
|                         subghz_rx_end(subghz_receiver->worker); |  | ||||||
|                         model->real_frequency = subghz_rx( |  | ||||||
|                             subghz_receiver->worker, subghz_frequencies[model->frequency]); |  | ||||||
|                         subghz_receiver->hopper_state = SubGhzHopperStateOFF; |  | ||||||
|                     } else { |  | ||||||
|                         osTimerStart(subghz_receiver->timer, 1024 / 10); |  | ||||||
|                         subghz_receiver->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|                     } |  | ||||||
|                     if(subghz_history_get_item(model->history) == 0) { |  | ||||||
|                         model->scene = ReceiverSceneStart; |  | ||||||
|                     } else { |  | ||||||
|                         model->scene = ReceiverSceneMain; |  | ||||||
|                     } |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context); |  | ||||||
|         } else { |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     bool model_updated = false; |  | ||||||
| 
 |  | ||||||
|                     if(event->key == InputKeyLeft) { |  | ||||||
|                         if(model->frequency > 0) model->frequency--; |  | ||||||
|                         model_updated = true; |  | ||||||
|                     } else if(event->key == InputKeyRight) { |  | ||||||
|                         if(model->frequency < subghz_frequencies_count) model->frequency++; |  | ||||||
|                         model_updated = true; |  | ||||||
|                     } |  | ||||||
|                     if(model_updated) { |  | ||||||
|                         model->real_frequency = subghz_frequencies[model->frequency]; |  | ||||||
|                     } |  | ||||||
|                     return model_updated; |  | ||||||
|                 }); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     case ReceiverSceneStart: |  | ||||||
|         if(event->type != InputTypeShort) return false; |  | ||||||
|         if(event->key == InputKeyBack) { |  | ||||||
|             return false; |  | ||||||
|         } else if(event->key == InputKeyLeft) { |  | ||||||
|             subghz_receiver->hopper_state = SubGhzHopperStatePause; |  | ||||||
|             with_view_model( |  | ||||||
|                 subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                     model->temp_frequency = model->frequency; |  | ||||||
|                     model->scene = ReceiverSceneConfig; |  | ||||||
|                     return true; |  | ||||||
|                 }); |  | ||||||
|             subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context); |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     subghz_receiver_update_offset(subghz_receiver); |     subghz_receiver_update_offset(subghz_receiver); | ||||||
|     if(scene != ReceiverSceneInfo) { |  | ||||||
|         with_view_model( |  | ||||||
|             subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|                 if(subghz_history_get_text_space_left(model->history, NULL)) { |  | ||||||
|                     subghz_receiver_history_full(subghz_receiver); |  | ||||||
|                 } |  | ||||||
|                 return false; |  | ||||||
|             }); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_text_callback(string_t text, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubghzReceiver* subghz_receiver = context; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             string_set(model->text, text); |  | ||||||
|             model->scene = ReceiverSceneMain; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_receiver_protocol_callback(SubGhzProtocolCommon* parser, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubghzReceiver* subghz_receiver = context; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             model->protocol_result = parser; |  | ||||||
|             subghz_history_set_frequency_preset( |  | ||||||
|                 model->history, |  | ||||||
|                 model->history_item, |  | ||||||
|                 model->real_frequency, |  | ||||||
|                 FuriHalSubGhzPresetOok650Async); |  | ||||||
|             subghz_history_add_to_history(model->history, parser); |  | ||||||
| 
 |  | ||||||
|             model->history_item = subghz_history_get_item(model->history); |  | ||||||
|             model->scene = ReceiverSceneMain; |  | ||||||
|             if(subghz_history_get_text_space_left(model->history, NULL)) { |  | ||||||
|                 subghz_receiver_history_full(subghz_receiver); |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
|     subghz_protocol_reset(subghz_receiver->protocol); |  | ||||||
|     subghz_receiver_update_offset(subghz_receiver); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void subghz_receiver_timer_callback(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubghzReceiver* subghz_receiver = context; |  | ||||||
| 
 |  | ||||||
|     switch(subghz_receiver->hopper_state) { |  | ||||||
|     case SubGhzHopperStatePause: |  | ||||||
|         return; |  | ||||||
|         break; |  | ||||||
|     case SubGhzHopperStateOFF: |  | ||||||
|         osTimerStop(subghz_receiver->timer); |  | ||||||
|         return; |  | ||||||
|         break; |  | ||||||
|     case SubGhzHopperStateRSSITimeOut: |  | ||||||
|         if(subghz_receiver->hopper_timeout != 0) { |  | ||||||
|             subghz_receiver->hopper_timeout--; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     float rssi = -127.0f; |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             if(subghz_receiver->hopper_state != SubGhzHopperStateRSSITimeOut) { |  | ||||||
|                 // See RSSI Calculation timings in CC1101 17.3 RSSI
 |  | ||||||
|                 rssi = furi_hal_subghz_get_rssi(); |  | ||||||
| 
 |  | ||||||
|                 // Stay if RSSI is high enough
 |  | ||||||
|                 if(rssi > -90.0f) { |  | ||||||
|                     subghz_receiver->hopper_timeout = 10; |  | ||||||
|                     subghz_receiver->hopper_state = SubGhzHopperStateRSSITimeOut; |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 subghz_receiver->hopper_state = SubGhzHopperStateRunnig; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Select next frequency
 |  | ||||||
|             if(model->frequency < COUNT_FREQUNCY_HOPPER - 1) { |  | ||||||
|                 model->frequency++; |  | ||||||
|             } else { |  | ||||||
|                 model->frequency = 0; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Restart radio
 |  | ||||||
|             furi_hal_subghz_idle(); |  | ||||||
|             subghz_protocol_reset(subghz_receiver->protocol); |  | ||||||
|             model->real_frequency = furi_hal_subghz_set_frequency_and_path( |  | ||||||
|                 subghz_frequencies_hopper[model->frequency]); |  | ||||||
|             furi_hal_subghz_rx(); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_receiver_enter(void* context) { | void subghz_receiver_enter(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubghzReceiver* subghz_receiver = context; |     //SubghzReceiver* subghz_receiver = context;
 | ||||||
|     //Start CC1101 Rx
 |  | ||||||
|     subghz_begin(FuriHalSubGhzPresetOok650Async); |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             subghz_rx_end(subghz_receiver->worker); |  | ||||||
|             model->frequency = subghz_frequencies_433_92; |  | ||||||
|             model->real_frequency = |  | ||||||
|                 subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]); |  | ||||||
|             if(subghz_history_get_item(model->history) == 0) { |  | ||||||
|                 model->scene = ReceiverSceneStart; |  | ||||||
|             } else { |  | ||||||
|                 model->scene = ReceiverSceneMain; |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
|     subghz_protocol_enable_dump( |  | ||||||
|         subghz_receiver->protocol, subghz_receiver_protocol_callback, subghz_receiver); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_exit(void* context) { | void subghz_receiver_exit(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubghzReceiver* subghz_receiver = context; |     SubghzReceiver* subghz_receiver = context; | ||||||
|     osTimerStop(subghz_receiver->timer); |  | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|             string_clean(model->text); |             string_clean(model->frequency_str); | ||||||
|             return true; |             string_clean(model->preset_str); | ||||||
|  |             string_clean(model->history_stat_str); | ||||||
|  |                 for | ||||||
|  |                     M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { | ||||||
|  |                         string_clear(item_menu->item_str); | ||||||
|  |                         item_menu->type = 0; | ||||||
|  |                     } | ||||||
|  |                 SubGhzReceiverMenuItemArray_clean(model->history->data); | ||||||
|  |                 model->idx = 0; | ||||||
|  |                 model->list_offset = 0; | ||||||
|  |                 model->history_item = 0; | ||||||
|  |                 return false; | ||||||
|         }); |         }); | ||||||
|     // Stop CC1101 Rx
 |  | ||||||
|     subghz_rx_end(subghz_receiver->worker); |  | ||||||
|     subghz_sleep(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubghzReceiver* subghz_receiver_alloc() { | SubghzReceiver* subghz_receiver_alloc() { | ||||||
| @ -616,14 +249,14 @@ SubghzReceiver* subghz_receiver_alloc() { | |||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|             string_init(model->text); |             string_init(model->frequency_str); | ||||||
|             model->history = subghz_history_alloc(); |             string_init(model->preset_str); | ||||||
|  |             string_init(model->history_stat_str); | ||||||
|  |             model->history = furi_alloc(sizeof(SubGhzReceiverHistory)); | ||||||
|  |             SubGhzReceiverMenuItemArray_init(model->history->data); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     subghz_receiver->timer = |  | ||||||
|         osTimerNew(subghz_receiver_timer_callback, osTimerPeriodic, subghz_receiver, NULL); |  | ||||||
|     subghz_receiver->hopper_state = SubGhzHopperStateOFF; |  | ||||||
|     return subghz_receiver; |     return subghz_receiver; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -632,11 +265,18 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver) { | |||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|             string_clear(model->text); |             string_clear(model->frequency_str); | ||||||
|             subghz_history_free(model->history); |             string_clear(model->preset_str); | ||||||
|  |             string_clear(model->history_stat_str); | ||||||
|  |                 for | ||||||
|  |                     M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { | ||||||
|  |                         string_clear(item_menu->item_str); | ||||||
|  |                         item_menu->type = 0; | ||||||
|  |                     } | ||||||
|  |                 SubGhzReceiverMenuItemArray_clear(model->history->data); | ||||||
|  |                 free(model->history); | ||||||
|                 return false; |                 return false; | ||||||
|         }); |         }); | ||||||
|     osTimerDelete(subghz_receiver->timer); |  | ||||||
|     view_free(subghz_receiver->view); |     view_free(subghz_receiver->view); | ||||||
|     free(subghz_receiver); |     free(subghz_receiver); | ||||||
| } | } | ||||||
| @ -646,43 +286,24 @@ View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver) { | |||||||
|     return subghz_receiver->view; |     return subghz_receiver->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver) { | uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver) { | ||||||
|     furi_assert(subghz_receiver); |     furi_assert(subghz_receiver); | ||||||
|     uint32_t frequency; |     uint32_t idx = 0; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|             frequency = subghz_history_get_frequency(model->history, model->idx); |             idx = model->idx; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
|     return frequency; |     return idx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver) { | void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx) { | ||||||
|     furi_assert(subghz_receiver); |     furi_assert(subghz_receiver); | ||||||
|     FuriHalSubGhzPreset preset; |  | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |         subghz_receiver->view, (SubghzReceiverModel * model) { | ||||||
|             preset = subghz_history_get_preset(model->history, model->idx); |             model->idx = idx; | ||||||
|             return false; |             if(model->idx > 2) model->list_offset = idx - 2; | ||||||
|  |             return true; | ||||||
|         }); |         }); | ||||||
|     return preset; |     subghz_receiver_update_offset(subghz_receiver); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output) { |  | ||||||
|     furi_assert(subghz_receiver); |  | ||||||
|     uint32_t frequency; |  | ||||||
|     uint32_t preset; |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_receiver->view, (SubghzReceiverModel * model) { |  | ||||||
|             frequency = subghz_history_get_frequency(model->history, model->idx); |  | ||||||
|             preset = (uint32_t)subghz_history_get_preset(model->history, model->idx); |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     string_cat_printf( |  | ||||||
|         output, |  | ||||||
|         "Frequency: %d\n" |  | ||||||
|         "Preset: %d\n", |  | ||||||
|         (int)frequency, |  | ||||||
|         (int)preset); |  | ||||||
| } | } | ||||||
| @ -1,21 +1,11 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> |  | ||||||
| #include <lib/subghz/protocols/subghz_protocol.h> |  | ||||||
| #include <lib/subghz/subghz_worker.h> |  | ||||||
| #include "../subghz_history.h" |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubghzReceverEventOK, |     SubghzReceverEventOK, | ||||||
|     SubghzReceverEventConfig, |     SubghzReceverEventConfig, | ||||||
|     SubghzReceverEventMain, |  | ||||||
|     SubghzReceverEventSave, |  | ||||||
|     SubghzReceverEventBack, |     SubghzReceverEventBack, | ||||||
|     SubghzReceverEventMore, |  | ||||||
|     SubghzReceverEventSendStart, |  | ||||||
|     SubghzReceverEventSendStop, |  | ||||||
|     SubghzReceverEventSendHistoryFull, |  | ||||||
| } SubghzReceverEvent; | } SubghzReceverEvent; | ||||||
| 
 | 
 | ||||||
| typedef struct SubghzReceiver SubghzReceiver; | typedef struct SubghzReceiver SubghzReceiver; | ||||||
| @ -33,17 +23,19 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver); | |||||||
| 
 | 
 | ||||||
| View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver); | View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver); | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_set_protocol( | void subghz_receiver_add_data_statusbar( | ||||||
|     SubghzReceiver* subghz_receiver, |     SubghzReceiver* subghz_receiver, | ||||||
|     SubGhzProtocolCommon* protocol_result, |     const char* frequency_str, | ||||||
|     SubGhzProtocol* protocol); |     const char* preset_str, | ||||||
|  |     const char* history_stat_str); | ||||||
| 
 | 
 | ||||||
| SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver); | void subghz_receiver_add_item_to_menu( | ||||||
|  |     SubghzReceiver* subghz_receiver, | ||||||
|  |     const char* name, | ||||||
|  |     uint8_t type); | ||||||
| 
 | 
 | ||||||
| void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker); | uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver); | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver); | void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx); | ||||||
| 
 | 
 | ||||||
| FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver); | void subghz_receiver_exit(void* context); | ||||||
| 
 |  | ||||||
| void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output); |  | ||||||
| @ -1,13 +1,8 @@ | |||||||
| #include "subghz_transmitter.h" | #include "subghz_transmitter.h" | ||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| 
 | 
 | ||||||
| #include <math.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi-hal.h> |  | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <notification/notification-messages.h> |  | ||||||
| #include <lib/subghz/protocols/subghz_protocol_keeloq.h> |  | ||||||
| 
 | 
 | ||||||
| struct SubghzTransmitter { | struct SubghzTransmitter { | ||||||
|     View* view; |     View* view; | ||||||
| @ -16,11 +11,10 @@ struct SubghzTransmitter { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     string_t text; |     string_t frequency_str; | ||||||
|     uint16_t scene; |     string_t preset_str; | ||||||
|     uint32_t real_frequency; |     string_t key_str; | ||||||
|     FuriHalSubGhzPreset preset; |     uint8_t show_button; | ||||||
|     SubGhzProtocolCommon* protocol; |  | ||||||
| } SubghzTransmitterModel; | } SubghzTransmitterModel; | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_set_callback( | void subghz_transmitter_set_callback( | ||||||
| @ -33,24 +27,19 @@ void subghz_transmitter_set_callback( | |||||||
|     subghz_transmitter->context = context; |     subghz_transmitter->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_set_protocol( | void subghz_transmitter_add_data_to_show( | ||||||
|     SubghzTransmitter* subghz_transmitter, |     SubghzTransmitter* subghz_transmitter, | ||||||
|     SubGhzProtocolCommon* protocol) { |     const char* key_str, | ||||||
|  |     const char* frequency_str, | ||||||
|  |     const char* preset_str, | ||||||
|  |     uint8_t show_button) { | ||||||
|  |     furi_assert(subghz_transmitter); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||||
|             model->protocol = protocol; |             string_set(model->key_str, key_str); | ||||||
|             return true; |             string_set(model->frequency_str, frequency_str); | ||||||
|         }); |             string_set(model->preset_str, preset_str); | ||||||
| } |             model->show_button = show_button; | ||||||
| 
 |  | ||||||
| void subghz_transmitter_set_frequency_preset( |  | ||||||
|     SubghzTransmitter* subghz_transmitter, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     FuriHalSubGhzPreset preset) { |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |  | ||||||
|             model->real_frequency = frequency; |  | ||||||
|             model->preset = preset; |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -87,26 +76,13 @@ static void subghz_transmitter_button_right(Canvas* canvas, const char* str) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_draw(Canvas* canvas, SubghzTransmitterModel* model) { | void subghz_transmitter_draw(Canvas* canvas, SubghzTransmitterModel* model) { | ||||||
|     char buffer[64]; |  | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_set_font(canvas, FontSecondary); |     canvas_set_font(canvas, FontSecondary); | ||||||
|     elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text)); |     elements_multiline_text(canvas, 0, 8, string_get_cstr(model->key_str)); | ||||||
|     snprintf( |     canvas_draw_str(canvas, 78, 8, string_get_cstr(model->frequency_str)); | ||||||
|         buffer, |     canvas_draw_str(canvas, 113, 8, string_get_cstr(model->preset_str)); | ||||||
|         sizeof(buffer), |     if(model->show_button) subghz_transmitter_button_right(canvas, "Send"); | ||||||
|         "%03ld.%03ld", |  | ||||||
|         model->real_frequency / 1000000 % 1000, |  | ||||||
|         model->real_frequency / 1000 % 1000); |  | ||||||
|     canvas_draw_str(canvas, 90, 8, buffer); |  | ||||||
| 
 |  | ||||||
|     if(model->protocol && model->protocol->get_upload_protocol) { |  | ||||||
|         if((!strcmp(model->protocol->name, "KeeLoq")) && |  | ||||||
|            (!strcmp(subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         subghz_transmitter_button_right(canvas, "Send"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_transmitter_input(InputEvent* event, void* context) { | bool subghz_transmitter_input(InputEvent* event, void* context) { | ||||||
| @ -114,26 +90,25 @@ bool subghz_transmitter_input(InputEvent* event, void* context) { | |||||||
|     SubghzTransmitter* subghz_transmitter = context; |     SubghzTransmitter* subghz_transmitter = context; | ||||||
|     bool can_be_sent = false; |     bool can_be_sent = false; | ||||||
| 
 | 
 | ||||||
|     if(event->key == InputKeyBack) { |     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||||
|  |         with_view_model( | ||||||
|  |             subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||||
|  |                 string_clean(model->frequency_str); | ||||||
|  |                 string_clean(model->preset_str); | ||||||
|  |                 string_clean(model->key_str); | ||||||
|  |                 model->show_button = 0; | ||||||
|  |                 return false; | ||||||
|  |             }); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||||
|             if(model->protocol && model->protocol->get_upload_protocol) { |             if(model->show_button) { | ||||||
|                 if((!strcmp(model->protocol->name, "KeeLoq")) && |  | ||||||
|                    (!strcmp( |  | ||||||
|                        subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|                 can_be_sent = true; |                 can_be_sent = true; | ||||||
|             } |             } | ||||||
|             //can_be_sent = (model->protocol && model->protocol->get_upload_protocol);
 |  | ||||||
|             string_clean(model->text); |  | ||||||
|             model->protocol->to_string(model->protocol, model->text); |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
|     //if(event->type != InputTypeShort) return false;
 |  | ||||||
| 
 | 
 | ||||||
|     if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { |     if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { | ||||||
|         subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context); |         subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context); | ||||||
| @ -146,37 +121,14 @@ bool subghz_transmitter_input(InputEvent* event, void* context) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_text_callback(string_t text, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SubghzTransmitter* subghz_transmitter = context; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |  | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |  | ||||||
|             string_set(model->text, text); |  | ||||||
|             model->scene = 0; |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void subghz_transmitter_enter(void* context) { | void subghz_transmitter_enter(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubghzTransmitter* subghz_transmitter = context; |     // SubghzTransmitter* subghz_transmitter = context;
 | ||||||
|     with_view_model( |  | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |  | ||||||
|             string_clean(model->text); |  | ||||||
|             model->protocol->to_string(model->protocol, model->text); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_exit(void* context) { | void subghz_transmitter_exit(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubghzTransmitter* subghz_transmitter = context; |     // SubghzTransmitter* subghz_transmitter = context;
 | ||||||
|     with_view_model( |  | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |  | ||||||
|             string_clean(model->text); |  | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubghzTransmitter* subghz_transmitter_alloc() { | SubghzTransmitter* subghz_transmitter_alloc() { | ||||||
| @ -194,7 +146,9 @@ SubghzTransmitter* subghz_transmitter_alloc() { | |||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||||
|             string_init(model->text); |             string_init(model->frequency_str); | ||||||
|  |             string_init(model->preset_str); | ||||||
|  |             string_init(model->key_str); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
|     return subghz_transmitter; |     return subghz_transmitter; | ||||||
| @ -205,7 +159,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter) { | |||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         subghz_transmitter->view, (SubghzTransmitterModel * model) { |         subghz_transmitter->view, (SubghzTransmitterModel * model) { | ||||||
|             string_clear(model->text); |             string_clear(model->frequency_str); | ||||||
|  |             string_clear(model->preset_str); | ||||||
|  |             string_clear(model->key_str); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
|     view_free(subghz_transmitter->view); |     view_free(subghz_transmitter->view); | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| #include <lib/subghz/protocols/subghz_protocol_common.h> |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubghzTransmitterEventSendStart, |     SubghzTransmitterEventSendStart, | ||||||
| @ -25,10 +24,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter); | |||||||
| 
 | 
 | ||||||
| View* subghz_transmitter_get_view(SubghzTransmitter* subghz_transmitter); | View* subghz_transmitter_get_view(SubghzTransmitter* subghz_transmitter); | ||||||
| 
 | 
 | ||||||
| void subghz_transmitter_set_protocol( | void subghz_transmitter_add_data_to_show( | ||||||
|     SubghzTransmitter* subghz_transmitter, |     SubghzTransmitter* subghz_transmitter, | ||||||
|     SubGhzProtocolCommon* protocol); |     const char* key_str, | ||||||
| void subghz_transmitter_set_frequency_preset( |     const char* frequency_str, | ||||||
|     SubghzTransmitter* subghz_transmitter, |     const char* preset_str, | ||||||
|     uint32_t frequency, |     uint8_t show_button); | ||||||
|     FuriHalSubGhzPreset preset); |  | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include "test_data/irda_samsung_test_data.srcdata" | #include "test_data/irda_samsung_test_data.srcdata" | ||||||
| #include "test_data/irda_rc6_test_data.srcdata" | #include "test_data/irda_rc6_test_data.srcdata" | ||||||
| #include "test_data/irda_rc5_test_data.srcdata" | #include "test_data/irda_rc5_test_data.srcdata" | ||||||
|  | #include "test_data/irda_sirc_test_data.srcdata" | ||||||
| 
 | 
 | ||||||
| #define RUN_ENCODER(data, expected) \ | #define RUN_ENCODER(data, expected) \ | ||||||
|     run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) |     run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) | ||||||
| @ -14,6 +15,8 @@ | |||||||
| #define RUN_DECODER(data, expected) \ | #define RUN_DECODER(data, expected) \ | ||||||
|     run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) |     run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) | ||||||
| 
 | 
 | ||||||
|  | #define RUN_ENCODER_DECODER(data) run_encoder_decoder((data), COUNT_OF(data)) | ||||||
|  | 
 | ||||||
| static IrdaDecoderHandler* decoder_handler; | static IrdaDecoderHandler* decoder_handler; | ||||||
| static IrdaEncoderHandler* encoder_handler; | static IrdaEncoderHandler* encoder_handler; | ||||||
| 
 | 
 | ||||||
| @ -33,27 +36,44 @@ static void compare_message_results( | |||||||
|     mu_check(message_decoded->protocol == message_expected->protocol); |     mu_check(message_decoded->protocol == message_expected->protocol); | ||||||
|     mu_check(message_decoded->command == message_expected->command); |     mu_check(message_decoded->command == message_expected->command); | ||||||
|     mu_check(message_decoded->address == message_expected->address); |     mu_check(message_decoded->address == message_expected->address); | ||||||
|  |     if((message_expected->protocol == IrdaProtocolSIRC) || | ||||||
|  |        (message_expected->protocol == IrdaProtocolSIRC15) || | ||||||
|  |        (message_expected->protocol == IrdaProtocolSIRC20)) { | ||||||
|  |         mu_check(message_decoded->repeat == false); | ||||||
|  |     } else { | ||||||
|         mu_check(message_decoded->repeat == message_expected->repeat); |         mu_check(message_decoded->repeat == message_expected->repeat); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | /* Encodes signal and merges same levels (high+high, low+low) */ | ||||||
|     run_encoder_fill_array(IrdaEncoderHandler* handler, uint32_t* timings, uint32_t* timings_len) { | static void run_encoder_fill_array( | ||||||
|  |     IrdaEncoderHandler* handler, | ||||||
|  |     uint32_t* timings, | ||||||
|  |     uint32_t* timings_len, | ||||||
|  |     bool* start_level) { | ||||||
|     uint32_t duration = 0; |     uint32_t duration = 0; | ||||||
|     bool level = false; // start from space
 |     bool level = false; | ||||||
|     bool level_read; |     bool level_read; | ||||||
|     IrdaStatus status = IrdaStatusError; |     IrdaStatus status = IrdaStatusError; | ||||||
|     int i = 0; |     int i = 0; | ||||||
|  |     bool first = true; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         status = irda_encode(handler, &duration, &level_read); |         status = irda_encode(handler, &duration, &level_read); | ||||||
|         if(level_read != level) { |         if(first) { | ||||||
|             level = level_read; |             if(start_level) *start_level = level_read; | ||||||
|  |             first = false; | ||||||
|  |             timings[0] = 0; | ||||||
|  |         } else if(level_read != level) { | ||||||
|             ++i; |             ++i; | ||||||
|  |             furi_assert(i < *timings_len); | ||||||
|  |             timings[i] = 0; | ||||||
|         } |         } | ||||||
|  |         level = level_read; | ||||||
|         timings[i] += duration; |         timings[i] += duration; | ||||||
|  | 
 | ||||||
|         furi_assert((status == IrdaStatusOk) || (status == IrdaStatusDone)); |         furi_assert((status == IrdaStatusOk) || (status == IrdaStatusDone)); | ||||||
|         if(status == IrdaStatusDone) break; |         if(status == IrdaStatusDone) break; | ||||||
|         furi_assert(i < *timings_len); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     *timings_len = i + 1; |     *timings_len = i + 1; | ||||||
| @ -66,8 +86,9 @@ static void run_encoder( | |||||||
|     const uint32_t expected_timings[], |     const uint32_t expected_timings[], | ||||||
|     uint32_t expected_timings_len) { |     uint32_t expected_timings_len) { | ||||||
|     uint32_t* timings = 0; |     uint32_t* timings = 0; | ||||||
|     uint32_t timings_len = 0; |     uint32_t timings_len = 200; | ||||||
|     uint32_t j = 0; |     uint32_t j = 0; | ||||||
|  |     timings = furi_alloc(sizeof(uint32_t) * timings_len); | ||||||
| 
 | 
 | ||||||
|     for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { |     for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { | ||||||
|         const IrdaMessage* message = &input_messages[message_counter]; |         const IrdaMessage* message = &input_messages[message_counter]; | ||||||
| @ -76,44 +97,51 @@ static void run_encoder( | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         timings_len = 200; |         timings_len = 200; | ||||||
|         timings = furi_alloc(sizeof(uint32_t) * timings_len); |         run_encoder_fill_array(encoder_handler, timings, &timings_len, NULL); | ||||||
|         run_encoder_fill_array(encoder_handler, timings, &timings_len); |  | ||||||
|         furi_assert(timings_len <= 200); |         furi_assert(timings_len <= 200); | ||||||
| 
 | 
 | ||||||
|         for(int i = 0; i < timings_len; ++i, ++j) { |         for(int i = 0; i < timings_len; ++i, ++j) { | ||||||
|             mu_check(MATCH_BIT_TIMING(timings[i], expected_timings[j], 120)); |             mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120)); | ||||||
|             mu_assert(j < expected_timings_len, "encoded more timings than expected"); |             mu_assert(j < expected_timings_len, "encoded more timings than expected"); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         free(timings); |  | ||||||
|     } |     } | ||||||
|  |     free(timings); | ||||||
|     mu_assert(j == expected_timings_len, "encoded less timings than expected"); |     mu_assert(j == expected_timings_len, "encoded less timings than expected"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t input_messages_len) { | static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t input_messages_len) { | ||||||
|     uint32_t* timings = 0; |     uint32_t* timings = 0; | ||||||
|     uint32_t timings_len = 0; |     uint32_t timings_len = 200; | ||||||
|     bool level = false; |     bool level = false; | ||||||
|  |     timings = furi_alloc(sizeof(uint32_t) * timings_len); | ||||||
| 
 | 
 | ||||||
|     for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { |     for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { | ||||||
|         const IrdaMessage* message_encoded = &input_messages[message_counter]; |         const IrdaMessage* message_encoded = &input_messages[message_counter]; | ||||||
|         if(!message_encoded->repeat) { |         if(!message_encoded->repeat) { | ||||||
|             irda_reset_encoder(encoder_handler, message_encoded); |             irda_reset_encoder(encoder_handler, message_encoded); | ||||||
|             level = false; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         timings_len = 200; |         timings_len = 200; | ||||||
|         timings = furi_alloc(sizeof(uint32_t) * timings_len); |         run_encoder_fill_array(encoder_handler, timings, &timings_len, &level); | ||||||
|         run_encoder_fill_array(encoder_handler, timings, &timings_len); |  | ||||||
|         furi_assert(timings_len <= 200); |         furi_assert(timings_len <= 200); | ||||||
| 
 | 
 | ||||||
|         const IrdaMessage* message_decoded = 0; |         const IrdaMessage* message_decoded = 0; | ||||||
|         for(int i = 0; i < timings_len; ++i) { |         for(int i = 0; i < timings_len; ++i) { | ||||||
|             message_decoded = irda_decode(decoder_handler, level, timings[i]); |             message_decoded = irda_decode(decoder_handler, level, timings[i]); | ||||||
|             if(i < timings_len - 1) |             if((i == timings_len - 2) && level && message_decoded) { | ||||||
|  |                 /* In case we end with space timing - message can be decoded at last mark.
 | ||||||
|  |                  * Exception - SIRC protocol, which has variable message length (12/15/20), | ||||||
|  |                  * and decoder recognizes protocol by silence time before next message | ||||||
|  |                  * or by timeout (irda_check_decoder_ready()). */ | ||||||
|  |                 break; | ||||||
|  |             } else if(i < timings_len - 1) { | ||||||
|                 mu_check(!message_decoded); |                 mu_check(!message_decoded); | ||||||
|             else |             } else { | ||||||
|  |                 if(!message_decoded) { | ||||||
|  |                     message_decoded = irda_check_decoder_ready(decoder_handler); | ||||||
|  |                 } | ||||||
|                 mu_check(message_decoded); |                 mu_check(message_decoded); | ||||||
|  |             } | ||||||
|             level = !level; |             level = !level; | ||||||
|         } |         } | ||||||
|         if(message_decoded) { |         if(message_decoded) { | ||||||
| @ -121,9 +149,8 @@ static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t inp | |||||||
|         } else { |         } else { | ||||||
|             mu_check(0); |             mu_check(0); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         free(timings); |  | ||||||
|     } |     } | ||||||
|  |     free(timings); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void run_decoder( | static void run_decoder( | ||||||
| @ -131,21 +158,49 @@ static void run_decoder( | |||||||
|     uint32_t input_delays_len, |     uint32_t input_delays_len, | ||||||
|     const IrdaMessage* message_expected, |     const IrdaMessage* message_expected, | ||||||
|     uint32_t message_expected_len) { |     uint32_t message_expected_len) { | ||||||
|     const IrdaMessage* message_decoded = 0; |     IrdaMessage message_decoded_check_local; | ||||||
|     bool level = 0; |     bool level = 0; | ||||||
|     uint32_t message_counter = 0; |     uint32_t message_counter = 0; | ||||||
|  |     const IrdaMessage* message_decoded = 0; | ||||||
| 
 | 
 | ||||||
|     for(uint32_t i = 0; i < input_delays_len; ++i) { |     for(uint32_t i = 0; i < input_delays_len; ++i) { | ||||||
|  |         const IrdaMessage* message_decoded_check = 0; | ||||||
|  | 
 | ||||||
|  |         if(input_delays[i] > IRDA_RAW_RX_TIMING_DELAY_US) { | ||||||
|  |             message_decoded_check = irda_check_decoder_ready(decoder_handler); | ||||||
|  |             if(message_decoded_check) { | ||||||
|  |                 /* irda_decode() can reset message, but we have to call irda_decode() to perform real
 | ||||||
|  |                  * simulation: irda_check() by timeout, then irda_decode() when meet edge */ | ||||||
|  |                 message_decoded_check_local = *message_decoded_check; | ||||||
|  |                 message_decoded_check = &message_decoded_check_local; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         message_decoded = irda_decode(decoder_handler, level, input_delays[i]); |         message_decoded = irda_decode(decoder_handler, level, input_delays[i]); | ||||||
|         if(message_decoded) { | 
 | ||||||
|  |         if(message_decoded_check || message_decoded) { | ||||||
|  |             mu_assert( | ||||||
|  |                 !(message_decoded_check && message_decoded), | ||||||
|  |                 "both messages decoded: check_ready() and irda_decode()"); | ||||||
|  | 
 | ||||||
|  |             if(message_decoded_check) { | ||||||
|  |                 message_decoded = message_decoded_check; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             mu_assert(message_counter < message_expected_len, "decoded more than expected"); |             mu_assert(message_counter < message_expected_len, "decoded more than expected"); | ||||||
|             if(message_counter >= message_expected_len) break; |  | ||||||
|             compare_message_results(message_decoded, &message_expected[message_counter]); |             compare_message_results(message_decoded, &message_expected[message_counter]); | ||||||
|  | 
 | ||||||
|             ++message_counter; |             ++message_counter; | ||||||
|         } |         } | ||||||
|         level = !level; |         level = !level; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     message_decoded = irda_check_decoder_ready(decoder_handler); | ||||||
|  |     if(message_decoded) { | ||||||
|  |         compare_message_results(message_decoded, &message_expected[message_counter]); | ||||||
|  |         ++message_counter; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     mu_assert(message_counter == message_expected_len, "decoded less than expected"); |     mu_assert(message_counter == message_expected_len, "decoded less than expected"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -155,6 +210,7 @@ MU_TEST(test_decoder_samsung32) { | |||||||
| 
 | 
 | ||||||
| MU_TEST(test_mix) { | MU_TEST(test_mix) { | ||||||
|     RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2); |     RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1); | ||||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); |     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||||
|     // can use encoder data for decoding, but can't do opposite
 |     // can use encoder data for decoding, but can't do opposite
 | ||||||
|     RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1); |     RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1); | ||||||
| @ -162,12 +218,16 @@ MU_TEST(test_mix) { | |||||||
|     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); |     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); | ||||||
|     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); |     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); | ||||||
|     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); |     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2); | ||||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); |     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4); | ||||||
|     RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); |     RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); | ||||||
|     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); |     RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); | ||||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); |     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5); | ||||||
|     RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5); |     RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5); | ||||||
|     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); |     RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(test_decoder_nec1) { | MU_TEST(test_decoder_nec1) { | ||||||
| @ -191,6 +251,20 @@ MU_TEST(test_decoder_necext1) { | |||||||
|     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); |     RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | MU_TEST(test_encoder_sirc) { | ||||||
|  |     RUN_ENCODER(test_encoder_sirc_input1, test_encoder_sirc_expected1); | ||||||
|  |     RUN_ENCODER(test_encoder_sirc_input2, test_encoder_sirc_expected2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(test_decoder_sirc) { | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4); | ||||||
|  |     RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5); | ||||||
|  |     RUN_ENCODER_DECODER(test_sirc); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| MU_TEST(test_decoder_rc5) { | MU_TEST(test_decoder_rc5) { | ||||||
|     RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1); |     RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1); | ||||||
|     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); |     RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1); | ||||||
| @ -219,16 +293,19 @@ MU_TEST(test_encoder_rc6) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(test_encoder_decoder_all) { | MU_TEST(test_encoder_decoder_all) { | ||||||
|     run_encoder_decoder(test_nec_all, COUNT_OF(test_nec_all)); |     RUN_ENCODER_DECODER(test_nec); | ||||||
|     run_encoder_decoder(test_necext_all, COUNT_OF(test_necext_all)); |     RUN_ENCODER_DECODER(test_necext); | ||||||
|     run_encoder_decoder(test_samsung32_all, COUNT_OF(test_samsung32_all)); |     RUN_ENCODER_DECODER(test_samsung32); | ||||||
|     run_encoder_decoder(test_rc6_all, COUNT_OF(test_rc6_all)); |     RUN_ENCODER_DECODER(test_rc6); | ||||||
|     run_encoder_decoder(test_rc5_all, COUNT_OF(test_rc5_all)); |     RUN_ENCODER_DECODER(test_rc5); | ||||||
|  |     RUN_ENCODER_DECODER(test_sirc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST_SUITE(test_irda_decoder_encoder) { | MU_TEST_SUITE(test_irda_decoder_encoder) { | ||||||
|     MU_SUITE_CONFIGURE(&test_setup, &test_teardown); |     MU_SUITE_CONFIGURE(&test_setup, &test_teardown); | ||||||
| 
 | 
 | ||||||
|  |     MU_RUN_TEST(test_encoder_sirc); | ||||||
|  |     MU_RUN_TEST(test_decoder_sirc); | ||||||
|     MU_RUN_TEST(test_encoder_rc5x); |     MU_RUN_TEST(test_encoder_rc5x); | ||||||
|     MU_RUN_TEST(test_encoder_rc5); |     MU_RUN_TEST(test_encoder_rc5); | ||||||
|     MU_RUN_TEST(test_decoder_rc5); |     MU_RUN_TEST(test_decoder_rc5); | ||||||
|  | |||||||
| @ -178,7 +178,7 @@ const IrdaMessage test_decoder_nec_expected2[] = { | |||||||
|     {IrdaProtocolNEC,     0x00,      0x0A,   true}, |     {IrdaProtocolNEC,     0x00,      0x0A,   true}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const IrdaMessage test_nec_all[] = { | const IrdaMessage test_nec[] = { | ||||||
|     {IrdaProtocolNEC,     0x00,      0x00,  false}, |     {IrdaProtocolNEC,     0x00,      0x00,  false}, | ||||||
|     {IrdaProtocolNEC,     0x01,      0x00,  false}, |     {IrdaProtocolNEC,     0x01,      0x00,  false}, | ||||||
|     {IrdaProtocolNEC,     0x01,      0x80,  false}, |     {IrdaProtocolNEC,     0x01,      0x80,  false}, | ||||||
|  | |||||||
| @ -223,7 +223,7 @@ const IrdaMessage test_decoder_necext_expected1[] = { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const IrdaMessage test_necext_all[] = { | const IrdaMessage test_necext[] = { | ||||||
|     {IrdaProtocolNECext,     0x0000,      0x00,  false}, |     {IrdaProtocolNECext,     0x0000,      0x00,  false}, | ||||||
|     {IrdaProtocolNECext,     0x0001,      0x00,  false}, |     {IrdaProtocolNECext,     0x0001,      0x00,  false}, | ||||||
|     {IrdaProtocolNECext,     0x0001,      0x80,  false}, |     {IrdaProtocolNECext,     0x0001,      0x80,  false}, | ||||||
|  | |||||||
| @ -128,7 +128,7 @@ const IrdaMessage test_decoder_rc5_expected_all_repeats[] = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const IrdaMessage test_rc5_all[] = { | const IrdaMessage test_rc5[] = { | ||||||
|     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, |     {IrdaProtocolRC5,     0x1F,      0x3F,  false}, | ||||||
|     {IrdaProtocolRC5,     0x00,      0x00,  false}, |     {IrdaProtocolRC5,     0x00,      0x00,  false}, | ||||||
|     {IrdaProtocolRC5,     0x10,      0x01,  false}, |     {IrdaProtocolRC5,     0x10,      0x01,  false}, | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ const uint32_t test_encoder_rc6_expected1[] = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const IrdaMessage test_rc6_all[] = { | const IrdaMessage test_rc6[] = { | ||||||
|     {IrdaProtocolRC6,     0x00,      0x00,  false},     // t 0 |     {IrdaProtocolRC6,     0x00,      0x00,  false},     // t 0 | ||||||
|     {IrdaProtocolRC6,     0x80,      0x00,  false},     // t 1 |     {IrdaProtocolRC6,     0x80,      0x00,  false},     // t 1 | ||||||
|     {IrdaProtocolRC6,     0x80,      0x01,  false},     // t 0 |     {IrdaProtocolRC6,     0x80,      0x01,  false},     // t 0 | ||||||
|  | |||||||
| @ -221,7 +221,7 @@ const IrdaMessage test_decoder_samsung32_expected1[] = { | |||||||
|     {IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true}, |     {IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const IrdaMessage test_samsung32_all[] = { | const IrdaMessage test_samsung32[] = { | ||||||
|     {IrdaProtocolSamsung32,     0x00,      0x00,  false}, |     {IrdaProtocolSamsung32,     0x00,      0x00,  false}, | ||||||
|     {IrdaProtocolSamsung32,     0x01,      0x00,  false}, |     {IrdaProtocolSamsung32,     0x01,      0x00,  false}, | ||||||
|     {IrdaProtocolSamsung32,     0x01,      0x80,  false}, |     {IrdaProtocolSamsung32,     0x01,      0x80,  false}, | ||||||
|  | |||||||
| @ -0,0 +1,485 @@ | |||||||
|  | const uint32_t test_decoder_sirc_input1[] = {  /* 121 timings */ | ||||||
|  | 1000000,    2420, 608, 1194, 608, 596, 604, 1198, 603, 591, 610, 1192, 609, 596, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196, | ||||||
|  |     25957,  2426, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 599, 603, 591, 609, 595, 606, 598, 602, 592, 609, 596, 605, 1197, | ||||||
|  |     25960,  2423, 606, 1196, 606, 599, 602, 1200, 602, 592, 609, 1193, 609, 596, 606, 599, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194, | ||||||
|  | 1000000,    2422, 607, 1195, 607, 598, 603, 1199, 604, 590, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 593, 607, 1195, | ||||||
|  |     25955,  2418, 610, 1192, 610, 594, 606, 1196, 606, 599, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 609, 1193, | ||||||
|  |     25959,  2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 603, 591, 610, 595, 606, 598, 602, 1200, | ||||||
|  | 1000000,    2424, 605, 599, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 611, 1191, | ||||||
|  |     26586,  2425, 604, 590, 611, 593, 607, 598, 603, 591, 610, 595, 606, 1196, 606, 1196, 606, 599, 602, 592, 608, 596, 604, 590, 611, 1191, | ||||||
|  |     26586,  2424, 604, 590, 611, 593, 607, 598, 603, 591, 609, 595, 605, 1197, 605, 1197, 604, 590, 611, 593, 607, 597, 603, 591, 610, 1192, | ||||||
|  | 1000000,    2424, 604, 1198, 604, 590, 611, 1191, 610, 594, 606, 598, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194, | ||||||
|  |     25386,  2419, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 591, 610, 1192, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197, | ||||||
|  |     25385,  2421, 608, 1194, 608, 596, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 1194, 608, 596, 605, 589, 611, 594, 607, 597, 604, 1198, | ||||||
|  | 1000000,    2426, 603, 1199, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 596, 604, 590, 610, 594, 607, 1195, 606, 598, 603, 591, | ||||||
|  |     15078,  2419, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 610, 595, 606, 598, 602, 1200, 602, 592, 608, 597, 604, 590, 611, 594, 607, 597, 603, 1199, 603, 591, 609, 595, | ||||||
|  |     15075,  2422, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 1191, 611, 594, 607, 597, 603, 591, 610, 594, 606, 598, 602, 1200, 602, 592, 608, 596, | ||||||
|  | 1000000,    2422, 607, 1195, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192, 610, 594, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192, | ||||||
|  |     26585,  2426, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 1195, 607, 598, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193, | ||||||
|  |     26586,  2425, 604, 1198, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193, 608, 597, 605, 600, 601, 593, 607, 597, 604, 590, 610, 1192, | ||||||
|  | 1000000,    2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 609, 1193, 609, 595, 605, 599, | ||||||
|  |     11557,  2418, 611, 594, 607, 598, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 609, 595, | ||||||
|  |     11561,  2424, 604, 590, 610, 594, 607, 1195, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 600, 601, 1191, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598, | ||||||
|  | 1000000,    2424, 604, 590, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 606, 598, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 608, 596, 604, 590, 611, 1191, 611, 1191, 611, 1191, 611, 594, 607, 598, | ||||||
|  |     11559,  2427, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 1198, 604, 1198, 604, 1198, 604, 590, 610, 1192, 610, 595, 606, 1196, 606, 1196, 606, 1196, 606, 599, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 597, 605, 589, | ||||||
|  |     11567,  2418, 610, 595, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 1201, 601, 593, 608, 1194, 607, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 590, 611, 594, | ||||||
|  | 1000000,    2421, 608, 597, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 1197, 604, 590, 610, 1192, 611, 1191, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596, | ||||||
|  |     11561,  2424, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 607, 1195, 606, 1196, 606, 598, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 597, 603, 591, | ||||||
|  |     11564,  2421, 607, 597, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 609, 595, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 1201, 601, 593, 607, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596, | ||||||
|  | 1000000,    2420, 609, 595, 606, 598, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592, | ||||||
|  |     11565,  2420, 609, 595, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 599, 601, 593, | ||||||
|  |     11563,  2422, 607, 597, 603, 591, 609, 1193, 609, 1193, 608, 1194, 608, 1194, 608, 1194, 582, 623, 603, 1199, 603, 591, 610, 1202, 599, 1203, 599, 1203, 599, 595, 581, 623, 577, 1225, 601, 1201, 601, 1201, 601, 593, 582, 623, | ||||||
|  | 1000000,    2425, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 607, 597, 603, 601, 575, 1227, 599, 1203, 599, 1203, 573, 621, 580, 625, | ||||||
|  |     10931,  2426, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 1217, 585, 1217, 585, 1217, 585, 620, 580, 1222, 580, 624, 577, 1225, 577, 1225, 577, 1225, 577, 617, 583, 622, 580, 1222, 580, 1222, 579, 1223, 579, 625, 576, 618, | ||||||
|  |     10936,  2421, 583, 1219, 583, 1219, 582, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 621, 580, 1222, 579, 1223, 579, 1223, 579, 625, 576, 618, 583, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616, | ||||||
|  | 1000000,    2419, 584, 620, 580, 1222, 580, 624, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 582, 1220, 582, 622, 579, 1223, 578, 1224, 579, 1223, 578, 616, 585, 619, 581, 1221, 581, 1221, 581, 1221, 580, 624, 576, 618, | ||||||
|  |     11563,  2422, 582, 622, 579, 1223, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 1218, 583, 622, 579, 1223, 579, 625, 575, 1216, 585, 1217, 585, 1217, 585, 619, 581, 623, 577, 1225, 577, 1225, 577, 1225, 576, 618, 583, 621, | ||||||
|  |     11558,  2427, 577, 617, 584, 1218, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 647, 553, 1249, 553, 651, 549, 1253, 548, 1254, 549, 1253, 549, 645, 555, 649, 551, 1251, 551, 1251, 551, 1251, 550, 654, 546, 648, | ||||||
|  | 1000000,    2456, 548, 646, 554, 650, 551, 653, 547, 1255, 547, 1255, 547, 1255, 547, 1255, 547, 647, 554, 1248, 554, 650, 551, 1251, 551, 1251, 551, 1251, 551, 653, 547, 647, 554, 1248, 554, 1248, 554, 1248, 553, 651, 550, 644, | ||||||
|  |     12112,  2449, 555, 649, 551, 654, 547, 647, 554, 1248, 554, 1248, 553, 1249, 553, 1249, 553, 651, 549, 1253, 549, 645, 555, 1247, 555, 1247, 555, 1247, 554, 650, 550, 655, 547, 1244, 557, 1224, 578, 1224, 579, 615, 585, 619, | ||||||
|  |     12139,  2423, 580, 624, 576, 618, 582, 622, 578, 1224, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 581, 1221, 580, 624, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 581, 623, | ||||||
|  | 1000000,    2422, 582, 1220, 581, 623, 578, 616, 584, 1218, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 1222, 580, 624, 576, 1226, 577, 1225, 576, 1226, 576, 618, 583, 622, 579, 1223, 578, 1224, 577, 1225, 577, 617, 585, 619, | ||||||
|  |     11559,  2426, 577, 1225, 577, 617, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 579, 1223, 579, 625, 575, 650, 550, 1252, 550, 1252, 549, 1253, 549, 645, 556, 648, | ||||||
|  |     11531,  2453, 549, 1253, 548, 646, 555, 649, 551, 1251, 551, 1251, 550, 1252, 549, 1253, 549, 645, 556, 1246, 555, 649, 552, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 584, 1218, 584, 1218, 584, 620, 581, 623, | ||||||
|  | 1000000,    2428, 576, 618, 582, 1220, 583, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 619, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 620, 581, 623, | ||||||
|  |     11557,  2427, 577, 617, 584, 1218, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 1246, 555, 649, 552, 1250, 552, 1250, 551, 1251, 551, 653, 548, 646, 554, 1248, 555, 1247, 555, 1247, 555, 649, 551, 653, | ||||||
|  |     11528,  2447, 556, 648, 552, 1250, 552, 653, 548, 1254, 548, 1254, 547, 1245, 557, 1245, 557, 647, 553, 1249, 552, 652, 549, 1253, 548, 1254, 548, 1254, 548, 646, 555, 649, 551, 1251, 551, 1251, 551, 1251, 551, 643, 557, 647, | ||||||
|  | 1000000,    2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593, | ||||||
|  |     11563,  2422, 606, 598, 602, 592, 608, 1194, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 599, 601, 1201, 600, 1191, 611, 1191, 611, 593, 606, 598, | ||||||
|  |     11558,  2427, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 609, 595, | ||||||
|  | 1000000,    2424, 605, 1197, 604, 600, 600, 1191, 610, 1192, 610, 1192, 610, 1192, 609, 1193, 609, 595, 605, 1197, 606, 598, 602, 1200, 602, 1200, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, | ||||||
|  |     10937,  2420, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 608, 1194, 608, 1194, 608, 1194, 609, 595, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 608, 596, | ||||||
|  |     10936,  2420, 608, 1194, 608, 596, 604, 1198, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 1201, 601, 593, 607, 597, | ||||||
|  | 1000000,    2420, 609, 1193, 608, 1194, 608, 596, 579, 625, 600, 1202, 600, 1202, 600, 1202, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 600, 594, | ||||||
|  |     11561,  2423, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594, | ||||||
|  |     11563,  2421, 608, 1194, 608, 1194, 608, 596, 605, 599, 601, 1201, 602, 1200, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 1197, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 610, 1192, 610, 594, 607, 597, | ||||||
|  | 1000000,    2428, 576, 1226, 600, 1192, 610, 594, 606, 598, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 591, 609, 1193, | ||||||
|  |     25955,  2427, 601, 1190, 610, 1192, 610, 594, 606, 598, 602, 1200, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194, | ||||||
|  |     25957,  2425, 604, 1198, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 599, 601, 593, 607, 598, 603, 591, 610, 594, 606, 598, 602, 1200, | ||||||
|  |     25952,  2420, 608, 1194, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194, | ||||||
|  | 1000000,    2421, 607, 597, 603, 1199, 603, 591, 610, 594, 606, 1196, 605, 600, 576, 618, 583, 621, 604, 601, 575, 619, 606, 598, 603, 1199, | ||||||
|  |     26576,  2424, 605, 600, 576, 1226, 601, 593, 608, 596, 604, 1198, 604, 600, 600, 594, 607, 597, 603, 591, 609, 595, 606, 598, 602, 1200, | ||||||
|  |     26579,  2420, 583, 621, 605, 1197, 604, 600, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196, | ||||||
|  |     26584,  2426, 603, 591, 609, 1193, 609, 595, 605, 599, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193, | ||||||
|  | 1000000,    2418, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 600, 602, 592, 608, 1194, 608, 596, 605, 1197, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591, | ||||||
|  |     13368,  2419, 610, 594, 606, 598, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 601, 600, 594, | ||||||
|  |     13362,  2425, 604, 601, 600, 594, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 602, 592, 609, 1193, 609, 1193, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 1192, 610, 1192, 610, 594, 607, 597, | ||||||
|  | 1000000,    2427, 601, 1201, 601, 593, 582, 623, 603, 1199, 578, 1224, 603, 1199, 577, 617, 584, 620, 605, 1197, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 593, 582, 622, 603, 1199, 603, 1199, 578, 1224, 603, 591, 610, 594, | ||||||
|  |     12139,  2423, 605, 1197, 605, 600, 601, 593, 608, 1194, 608, 1194, 607, 1195, 607, 597, 604, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 583, 622, 604, 601, 600, 1202, 575, 1227, 601, 1201, 576, 618, 607, 597, | ||||||
|  |     12137,  2424, 604, 1198, 604, 590, 610, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 601, 600, 1202, 600, 594, 607, 1195, 606, 1196, 581, 1221, 607, 597, 603, 591, 610, 1202, 600, 1202, 576, 1226, 602, 592, 609, 595, | ||||||
|  | 1000000,    2422, 607, 1195, 607, 597, 603, 591, 585, 619, 606, 1196, 606, 1196, 581, 624, 577, 617, 609, 1193, 584, 621, 605, 1197, 604, 1198, 604, 1198, 604, 590, 610, 595, 581, 1221, 605, 1197, 605, 1197, 605, 599, 601, 593, | ||||||
|  |     12763,  2427, 603, 1199, 603, 591, 609, 595, 605, 599, 577, 1225, 602, 1200, 601, 593, 583, 622, 604, 1198, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594, | ||||||
|  |     12763,  2427, 602, 1200, 601, 593, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 593, 607, 597, 604, 1198, 604, 590, 610, 1192, 610, 1192, 611, 1191, 610, 594, 607, 598, 578, 1224, 603, 1199, 603, 1199, 603, 591, 610, 594, | ||||||
|  | 1000000,    2422, 607, 597, 603, 591, 610, 1192, 610, 594, 606, 1196, 580, 1222, 605, 600, 601, 593, 583, 1219, 608, 596, 579, 1223, 604, 1198, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 599, 602, 592, | ||||||
|  |     12765,  2425, 603, 591, 610, 594, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 593, 582, 623, 604, 1198, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 596, 605, 599, 601, 1201, 601, 1201, 601, 1201, 600, 594, 607, 597, | ||||||
|  |     12758,  2421, 607, 597, 603, 591, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 580, 1222, 605, 1197, 605, 1197, 605, 600, 600, 594, | ||||||
|  | 1000000,    2422, 580, 625, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 604, 600, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 578, 616, 609, 595, 606, 1196, 606, 1196, 606, 1196, 581, 624, 602, 592, | ||||||
|  |     12766,  2424, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 579, 625, 601, 593, 608, 1194, 608, 596, 604, 1198, 580, 1222, 605, 1197, 605, 599, 602, 592, 608, 1194, 609, 1193, 609, 1193, 609, 596, 605, 599, | ||||||
|  |     12759,  2420, 608, 597, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 598, 602, 592, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 601, 593, | ||||||
|  | 1000000,    2429, 600, 1202, 575, 1227, 599, 595, 606, 598, 602, 1200, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, | ||||||
|  |     12136,  2425, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 596, 604, 1198, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593, | ||||||
|  |     12137,  2424, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 604, 1198, 604, 600, 600, 594, | ||||||
|  | 1000000,    2429, 574, 1228, 573, 1229, 573, 1229, 573, 1229, 573, 621, 581, 624, 577, 617, 583, 622, 580, 1222, 579, 625, 576, 1226, 577, 1225, 576, 1226, 577, 617, 584, 620, 581, 1221, 580, 1222, 579, 1223, 579, 625, 577, 617, | ||||||
|  |     12142,  2419, 584, 1218, 584, 1218, 583, 1219, 583, 1219, 582, 623, 578, 616, 585, 619, 581, 623, 578, 1224, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 624, 576, 1226, 575, 1227, 500, 1353, 523, 620, 582, 622, /* failed, noise pollution 500, 1353 timings */ | ||||||
|  |     12134,  2427, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 583, 622, 579, 625, 576, 618, 583, 1219, 583, 673, 527, 1223, 579, 1223, 579, 1223, 579, 625, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616, | ||||||
|  |     12140,  2421, 582, 1220, 581, 1221, 581, 1221, 580, 1222, 580, 624, 576, 618, 582, 622, 578, 616, 585, 1217, 584, 620, 581, 1221, 580, 1222, 580, 1222, 580, 624, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 623, 578, 616, | ||||||
|  | 1000000,    2419, 584, 672, 528, 625, 577, 617, 583, 1219, 582, 1220, 581, 1221, 582, 622, 578, 616, 585, 1217, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 618, 583, 1219, 583, 1219, 582, 1220, 583, 621, 580, 624, | ||||||
|  |     12732,  2427, 577, 617, 584, 672, 528, 676, 524, 1226, 576, 1226, 576, 1226, 576, 618, 583, 621, 579, 1223, 579, 625, 575, 1227, 575, 1227, 575, 1227, 575, 671, 529, 675, 526, 1224, 578, 1224, 578, 1224, 578, 616, 585, 619, | ||||||
|  |     12738,  2421, 583, 621, 580, 624, 576, 618, 582, 1220, 582, 1220, 583, 1219, 583, 621, 579, 625, 576, 1226, 576, 670, 530, 1220, 582, 1220, 582, 1220, 581, 624, 577, 617, 584, 1218, 583, 1219, 583, 1219, 583, 622, 580, 624, | ||||||
|  | 1000000,    2430, 599, 1192, 609, 595, 605, 1197, 580, 624, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194, | ||||||
|  |     25958,  2423, 605, 1197, 604, 600, 575, 1227, 600, 594, 606, 1196, 606, 598, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 1199, | ||||||
|  |     25949,  2422, 607, 1195, 606, 598, 603, 1199, 602, 592, 608, 1194, 608, 596, 604, 600, 601, 593, 607, 597, 604, 600, 600, 594, 607, 1195, | ||||||
|  |     25958,  2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 596, 605, 600, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196, | ||||||
|  |     25957,  2425, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 596, 604, 1198, | ||||||
|  |     25956,  2426, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 608, 597, 605, 599, 601, 593, 607, 597, 603, 1199, | ||||||
|  |     25952,  2420, 609, 1193, 608, 596, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 1195, | ||||||
|  | 1000000,    2422, 583, 1219, 608, 596, 605, 1197, 580, 624, 601, 1201, 601, 593, 608, 596, 604, 600, 601, 593, 607, 597, 604, 590, 610, 1202, | ||||||
|  |     25955,  2427, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 1196, 607, 597, 603, 591, 609, 595, 606, 598, 602, 592, 609, 595, 605, 1197, | ||||||
|  |     25957,  2425, 604, 1198, 604, 590, 610, 1192, 610, 594, 581, 1221, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 597, 604, 1198, | ||||||
|  |     25954,  2418, 610, 1192, 610, 594, 606, 1196, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193, | ||||||
|  |     25958,  2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 1199, | ||||||
|  |     25953,  2419, 610, 1192, 609, 595, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 597, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194, | ||||||
|  |     25954,  2418, 611, 1191, 610, 594, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 591, 609, 1193, | ||||||
|  |     25958,  2424, 605, 1197, 605, 599, 601, 1201, 600, 594, 607, 1195, 607, 597, 603, 591, 609, 596, 605, 599, 602, 592, 608, 596, 605, 1197, | ||||||
|  |     25954,  2428, 601, 1190, 610, 594, 607, 1195, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193, | ||||||
|  |     25960,  2422, 607, 1195, 606, 598, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196, | ||||||
|  |     25957,  2424, 605, 1197, 604, 600, 600, 1202, 601, 593, 607, 1195, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 606, 1196, | ||||||
|  | 1000000,    2421, 608, 1194, 608, 596, 604, 1198, 604, 601, 575, 1227, 601, 593, 607, 598, 604, 600, 600, 594, 607, 598, 604, 601, 600, 1202, | ||||||
|  |     25953,  2419, 609, 1193, 609, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 599, 602, 592, 608, 1194, | ||||||
|  |     25958,  2424, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 1195, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 608, 596, 604, 1198, | ||||||
|  | 1000000,    2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196, | ||||||
|  |     25958,  2424, 605, 1197, 604, 600, 601, 1201, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 595, 607, 597, 603, 591, 610, 594, 607, 1195, | ||||||
|  |     25961,  2421, 608, 1194, 608, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193, | ||||||
|  | 1000000,    2429, 601, 1201, 601, 593, 607, 1195, 608, 596, 604, 1198, 604, 600, 601, 593, 607, 597, 604, 590, 611, 593, 607, 597, 604, 1198, | ||||||
|  |     25959,  2422, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 1194, | ||||||
|  |     25959,  2421, 608, 1194, 607, 597, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 602, 592, 608, 1194, | ||||||
|  |     25959,  2422, 608, 1194, 608, 596, 604, 1198, 604, 590, 611, 1201, 601, 593, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 609, 1193, | ||||||
|  |     25962,  2418, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 1199, 603, 591, 609, 595, 605, 599, 602, 592, 608, 596, 605, 599, 601, 1201, | ||||||
|  | 
 | ||||||
|  | 1000000,    2357, 610, 1183, 592, 1191, 614, 1179, 595, 1188, 618, 584, 613, 1180, 593, 588, 613, 1190, 612, 590, 605, 587, 613, 589, 605, 587, | ||||||
|  |     25398,  2355, 623, 1180, 591, 1181, 627, 1186, 589, 1183, 618, 584, 616, 1187, 587, 584, 614, 1189, 615, 587, 605, 587, 615, 587, 609, 593, | ||||||
|  | 1000000,    2383, 609, 1214, 600, 612, 580, 1213, 608, 614, 583, 1210, 605, 607, 586, 616, 611, 1192, 599, 613, 608, 614, 579, 613, 615, 607, | ||||||
|  |     26670,  2387, 601, 1212, 600, 612, 589, 1214, 603, 609, 586, 1217, 595, 607, 594, 618, 606, 1187, 602, 610, 609, 613, 588, 614, 610, 612, | ||||||
|  | 1000000,    2445, 582, 1221, 603, 548, 602, 1191, 609, 572, 602, 1191, 609, 542, 607, 544, 631, 1172, 603, 568, 606, 545, 605, 566, 608, 543, | ||||||
|  |     26263,  2414, 611, 1192, 607, 544, 606, 1197, 602, 569, 606, 1197, 602, 539, 611, 540, 635, 1168, 606, 565, 610, 541, 608, 563, 587, 564, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_decoder_sirc_expected1[] = { | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x60, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x60, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x60, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x65, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x65, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x65, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x410,  0x17, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x410,  0x17, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x410,  0x17, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x21, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x21, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x21, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7B, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7B, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7B, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x78, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x78, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x78, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x79, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x79, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x79, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7A, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7C, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7D, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7D, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x7D, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x73, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x73, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x73, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x13, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x13, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x13, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x13, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x12, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x12, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x12, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x12, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x30, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x30, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x30, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x39, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x39, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x39, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x31, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x31, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x31, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x34, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x34, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x34, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x32, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x32, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x32, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x33, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x33, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x33, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x0F, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x0F, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x0F, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x38, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x38, false}, | ||||||
|  |     {IrdaProtocolSIRC20,       0x73A,  0x38, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x10,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x2F, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x2F, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x15, false}, | ||||||
|  |     {IrdaProtocolSIRC,         0x01,   0x15, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //   command (0x55)    address (0x0A)       SIRC | ||||||
|  | //   1 0 1 0 1 0 1     0 1 0 1 0 | ||||||
|  | //   2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | // | ||||||
|  | //   command (0x7F)    address (0x1F)       SIRC | ||||||
|  | //   1 1 1 1 1 1 1     1 1 1 1 1 | ||||||
|  | //   2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | // | ||||||
|  | //   command (0x00)    address (0x00)       SIRC | ||||||
|  | //   0 0 0 0 0 0 0     0 0 0 0 0 | ||||||
|  | //   2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | // | ||||||
|  | //   command (0x53)    address (0x0D)       SIRC | ||||||
|  | //   1 1 0 0 1 0 1     1 0 1 1 0 | ||||||
|  | //   2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, | ||||||
|  | 
 | ||||||
|  | const uint32_t test_decoder_sirc_input2[] = { | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, /* failed - should be skipped */ | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */ | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | 1000000, 2400, 600, 1200, 600,  /* failed - should be skipped */ | ||||||
|  |          2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */ | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_decoder_sirc_expected2[] = { | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     /* failed - 13 data bits */ | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0x1F, 0x7F, false}, | ||||||
|  |     /* failed - 2 data bits */ | ||||||
|  |     {IrdaProtocolSIRC, 0x1F, 0x7F, false}, | ||||||
|  |     /* failed - sudden end */ | ||||||
|  |     {IrdaProtocolSIRC, 0x1F, 0x7F, false}, | ||||||
|  |     /* failed */ | ||||||
|  |     {IrdaProtocolSIRC, 0x0A, 0x55, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0x00, 0x00, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0x0D, 0x53, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //   command (0x13)    address (0xFD)       SIRC15 | ||||||
|  | //   1 1 0 0 1 0 0     1 0 1 1 1 1 1 1 | ||||||
|  | //   2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | // | ||||||
|  | //   command (0x53)    address (0x7D)       SIRC15 | ||||||
|  | //   1 1 0 0 1 0 1     1 0 1 1 1 1 1 0 | ||||||
|  | //   2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | // | ||||||
|  | //   command (0x53)    address (0x0D)       SIRC15 | ||||||
|  | //   1 1 0 0 1 0 1     1 0 1 1 0 0 0 0 | ||||||
|  | //   2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | 
 | ||||||
|  | const uint32_t test_decoder_sirc_input3[] = { | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  |   10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  |   10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  |   10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  |   10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, | ||||||
|  | 
 | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  |   10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_decoder_sirc_expected3[] = { | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x0D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x0D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x0D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x0D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x0D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0xFD, 0x13, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t test_decoder_sirc_input4[] = { | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_decoder_sirc_expected4[] = { | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false},      //    {IrdaProtocolSIRC20, 0x15, 0x3ED3, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t test_decoder_sirc_input5[] = { | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 
 | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | 10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_decoder_sirc_expected5[] = { | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0xFB5, 0x53, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_encoder_sirc_input1[] = { | ||||||
|  |     {IrdaProtocolSIRC, 0xA, 0x55, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t test_encoder_sirc_expected1[] = { | ||||||
|  | 10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_encoder_sirc_input2[] = { | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, true}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, true}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t test_encoder_sirc_expected2[] = { | ||||||
|  |     10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */ | ||||||
|  |     18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */ | ||||||
|  |     18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const IrdaMessage test_sirc[] = { | ||||||
|  |     {IrdaProtocolSIRC20, 0x1FFF, 0x7F, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0x1FFF, 0x7F, true}, | ||||||
|  |     {IrdaProtocolSIRC20, 0x1FFF, 0x7F, true}, | ||||||
|  |     {IrdaProtocolSIRC, 0x00, 0x00, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0x00, 0x00, true}, | ||||||
|  |     {IrdaProtocolSIRC, 0x00, 0x00, true}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0x1A, 0x22, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0x1A, 0x22, true}, | ||||||
|  |     {IrdaProtocolSIRC, 0x1A, 0x22, true}, | ||||||
|  |     {IrdaProtocolSIRC, 0x17, 0x0A, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, true}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x7D, 0x53, true}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x71, 0x0, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x15, 0x01, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x01, 0x15, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC20, 0xAA, 0x55, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0x331, 0x71, false}, | ||||||
|  | 
 | ||||||
|  |     {IrdaProtocolSIRC, 0x00, 0x00, false}, | ||||||
|  |     {IrdaProtocolSIRC, 0x1F, 0x7F, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0x00, 0x00, false}, | ||||||
|  |     {IrdaProtocolSIRC15, 0xFF, 0x7F, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0x00, 0x00, false}, | ||||||
|  |     {IrdaProtocolSIRC20, 0x1FFF, 0x7F, false}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| @ -4,7 +4,7 @@ include				$(PROJECT_ROOT)/assets/assets.mk | |||||||
| 
 | 
 | ||||||
| $(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER) | $(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER) | ||||||
| 	@echo "\tASSETS\t" $@ | 	@echo "\tASSETS\t" $@ | ||||||
| 	@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_COMPILED_DIR) | 	@$(ASSETS_COMPILLER) icons "$(ASSETS_SOURCE_DIR)" "$(ASSETS_COMPILED_DIR)" | ||||||
| 
 | 
 | ||||||
| clean: | clean: | ||||||
| 	@echo "\tCLEAN\t" | 	@echo "\tCLEAN\t" | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ | |||||||
| MEMORY | MEMORY | ||||||
| { | { | ||||||
| FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 32K | FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 32K | ||||||
| RAM1 (xrw)                 : ORIGIN = 0x20000004, LENGTH = 0x2FFFC | RAM1 (xrw)                 : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 | ||||||
| RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K | RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										189
									
								
								bootloader/targets/f7/furi-hal/furi-hal-gpio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								bootloader/targets/f7/furi-hal/furi-hal-gpio.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,189 @@ | |||||||
|  | #include <furi-hal-gpio.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #define GET_SYSCFG_EXTI_PORT(gpio)                \ | ||||||
|  |     (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \ | ||||||
|  |      ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \ | ||||||
|  |      ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \ | ||||||
|  |      ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \ | ||||||
|  |      ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \ | ||||||
|  |                            LL_SYSCFG_EXTI_PORTH) | ||||||
|  | 
 | ||||||
|  | #define GPIO_PIN_MAP(pin, prefix)               \ | ||||||
|  |     (((pin) == (LL_GPIO_PIN_0))  ? prefix##0 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_1))  ? prefix##1 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_2))  ? prefix##2 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_3))  ? prefix##3 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_4))  ? prefix##4 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_5))  ? prefix##5 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_6))  ? prefix##6 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_7))  ? prefix##7 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_8))  ? prefix##8 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_9))  ? prefix##9 :  \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ | ||||||
|  |      ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ | ||||||
|  |                                    prefix##15) | ||||||
|  | 
 | ||||||
|  | #define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) | ||||||
|  | #define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) | ||||||
|  | 
 | ||||||
|  | static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER]; | ||||||
|  | 
 | ||||||
|  | static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) { | ||||||
|  |     uint8_t pin_num = 0; | ||||||
|  |     for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) { | ||||||
|  |         if(gpio->pin & (1 << pin_num)) break; | ||||||
|  |     } | ||||||
|  |     return pin_num; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) { | ||||||
|  |     hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_init( | ||||||
|  |     const GpioPin* gpio, | ||||||
|  |     const GpioMode mode, | ||||||
|  |     const GpioPull pull, | ||||||
|  |     const GpioSpeed speed) { | ||||||
|  |     // we cannot set alternate mode in this function
 | ||||||
|  |     assert(mode != GpioModeAltFunctionPushPull); | ||||||
|  |     assert(mode != GpioModeAltFunctionOpenDrain); | ||||||
|  | 
 | ||||||
|  |     hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_init_ex( | ||||||
|  |     const GpioPin* gpio, | ||||||
|  |     const GpioMode mode, | ||||||
|  |     const GpioPull pull, | ||||||
|  |     const GpioSpeed speed, | ||||||
|  |     const GpioAltFn alt_fn) { | ||||||
|  |     uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); | ||||||
|  |     uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); | ||||||
|  |     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); | ||||||
|  | 
 | ||||||
|  |     // Configure gpio with interrupts disabled
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     // Set gpio speed
 | ||||||
|  |     if(speed == GpioSpeedLow) { | ||||||
|  |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||||
|  |     } else if(speed == GpioSpeedMedium) { | ||||||
|  |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||||
|  |     } else if(speed == GpioSpeedHigh) { | ||||||
|  |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||||
|  |     } else { | ||||||
|  |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |     } | ||||||
|  |     // Set gpio pull mode
 | ||||||
|  |     if(pull == GpioPullNo) { | ||||||
|  |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||||
|  |     } else if(pull == GpioPullUp) { | ||||||
|  |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||||
|  |     } else { | ||||||
|  |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||||
|  |     } | ||||||
|  |     // Set gpio mode
 | ||||||
|  |     if(mode >= GpioModeInterruptRise) { | ||||||
|  |         // Set pin in interrupt mode
 | ||||||
|  |         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |         LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line); | ||||||
|  |         if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) { | ||||||
|  |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|  |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|  |         } | ||||||
|  |         if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) { | ||||||
|  |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|  |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|  |         } | ||||||
|  |         if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { | ||||||
|  |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|  |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|  |         } | ||||||
|  |         if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { | ||||||
|  |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|  |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // Disable interrupt if it was set
 | ||||||
|  |         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||||
|  |            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||||
|  |             LL_EXTI_DisableIT_0_31(exti_line); | ||||||
|  |             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||||
|  |             LL_EXTI_DisableFallingTrig_0_31(exti_line); | ||||||
|  |         } | ||||||
|  |         // Set not interrupt pin modes
 | ||||||
|  |         if(mode == GpioModeInput) { | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |         } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |         } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |         } else if(mode == GpioModeAnalog) { | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|  |         // enable alternate mode
 | ||||||
|  |         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  | 
 | ||||||
|  |         // set alternate function
 | ||||||
|  |         if(hal_gpio_get_pin_num(gpio) < 8) { | ||||||
|  |             LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||||
|  |         } else { | ||||||
|  |             LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | ||||||
|  |     assert(gpio); | ||||||
|  |     assert(cb); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|  |     gpio_interrupt[pin_num].callback = cb; | ||||||
|  |     gpio_interrupt[pin_num].context = ctx; | ||||||
|  |     gpio_interrupt[pin_num].ready = true; | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_enable_int_callback(const GpioPin* gpio) { | ||||||
|  |     assert(gpio); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|  |     if(gpio_interrupt[pin_num].callback) { | ||||||
|  |         gpio_interrupt[pin_num].ready = true; | ||||||
|  |     } | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_disable_int_callback(const GpioPin* gpio) { | ||||||
|  |     assert(gpio); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|  |     gpio_interrupt[pin_num].ready = false; | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hal_gpio_remove_int_callback(const GpioPin* gpio) { | ||||||
|  |     assert(gpio); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|  |     gpio_interrupt[pin_num].callback = NULL; | ||||||
|  |     gpio_interrupt[pin_num].context = NULL; | ||||||
|  |     gpio_interrupt[pin_num].ready = false; | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
							
								
								
									
										264
									
								
								bootloader/targets/f7/furi-hal/furi-hal-gpio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								bootloader/targets/f7/furi-hal/furi-hal-gpio.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "main.h" | ||||||
|  | #include "stdbool.h" | ||||||
|  | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | #include <stm32wbxx_ll_system.h> | ||||||
|  | #include <stm32wbxx_ll_exti.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Number of gpio on one port | ||||||
|  |  */ | ||||||
|  | #define GPIO_NUMBER (16U) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Interrupt callback prototype | ||||||
|  |  */ | ||||||
|  | typedef void (*GpioExtiCallback)(void* ctx); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio interrupt type | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     GpioExtiCallback callback; | ||||||
|  |     void* context; | ||||||
|  |     volatile bool ready; | ||||||
|  | } GpioInterrupt; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio modes | ||||||
|  |  */ | ||||||
|  | typedef enum { | ||||||
|  |     GpioModeInput, | ||||||
|  |     GpioModeOutputPushPull, | ||||||
|  |     GpioModeOutputOpenDrain, | ||||||
|  |     GpioModeAltFunctionPushPull, | ||||||
|  |     GpioModeAltFunctionOpenDrain, | ||||||
|  |     GpioModeAnalog, | ||||||
|  |     GpioModeInterruptRise, | ||||||
|  |     GpioModeInterruptFall, | ||||||
|  |     GpioModeInterruptRiseFall, | ||||||
|  |     GpioModeEventRise, | ||||||
|  |     GpioModeEventFall, | ||||||
|  |     GpioModeEventRiseFall, | ||||||
|  | } GpioMode; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio pull modes | ||||||
|  |  */ | ||||||
|  | typedef enum { | ||||||
|  |     GpioPullNo, | ||||||
|  |     GpioPullUp, | ||||||
|  |     GpioPullDown, | ||||||
|  | } GpioPull; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio speed modes | ||||||
|  |  */ | ||||||
|  | typedef enum { | ||||||
|  |     GpioSpeedLow, | ||||||
|  |     GpioSpeedMedium, | ||||||
|  |     GpioSpeedHigh, | ||||||
|  |     GpioSpeedVeryHigh, | ||||||
|  | } GpioSpeed; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio alternate functions | ||||||
|  |  */ | ||||||
|  | typedef enum { | ||||||
|  |     GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */ | ||||||
|  |     GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */ | ||||||
|  |     GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */ | ||||||
|  |     GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */ | ||||||
|  |     GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */ | ||||||
|  |     GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */ | ||||||
|  |     GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */ | ||||||
|  |     GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */ | ||||||
|  |     GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */ | ||||||
|  |     GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */ | ||||||
|  |     GpioAltFn0SYS = 0, /*!< System Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */ | ||||||
|  |     GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */ | ||||||
|  |     GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */ | ||||||
|  |     GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */ | ||||||
|  |     GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */ | ||||||
|  |     GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */ | ||||||
|  |     GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */ | ||||||
|  |     GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */ | ||||||
|  | 
 | ||||||
|  |     GpioAltFnUnused = 16, /*!< just dummy value */ | ||||||
|  | } GpioAltFn; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Gpio structure | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     GPIO_TypeDef* port; | ||||||
|  |     uint16_t pin; | ||||||
|  | } GpioPin; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * GPIO initialization function, simple version | ||||||
|  |  * @param gpio  GpioPin | ||||||
|  |  * @param mode  GpioMode | ||||||
|  |  */ | ||||||
|  | void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * GPIO initialization function, normal version | ||||||
|  |  * @param gpio  GpioPin | ||||||
|  |  * @param mode  GpioMode | ||||||
|  |  * @param pull  GpioPull | ||||||
|  |  * @param speed GpioSpeed | ||||||
|  |  */ | ||||||
|  | void hal_gpio_init( | ||||||
|  |     const GpioPin* gpio, | ||||||
|  |     const GpioMode mode, | ||||||
|  |     const GpioPull pull, | ||||||
|  |     const GpioSpeed speed); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * GPIO initialization function, extended version | ||||||
|  |  * @param gpio  GpioPin | ||||||
|  |  * @param mode  GpioMode | ||||||
|  |  * @param pull  GpioPull | ||||||
|  |  * @param speed GpioSpeed | ||||||
|  |  * @param alt_fn GpioAltFn | ||||||
|  |  */ | ||||||
|  | void hal_gpio_init_ex( | ||||||
|  |     const GpioPin* gpio, | ||||||
|  |     const GpioMode mode, | ||||||
|  |     const GpioPull pull, | ||||||
|  |     const GpioSpeed speed, | ||||||
|  |     const GpioAltFn alt_fn); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Add and enable interrupt | ||||||
|  |  * @param gpio GpioPin | ||||||
|  |  * @param cb   GpioExtiCallback | ||||||
|  |  * @param ctx  context for callback | ||||||
|  |  */ | ||||||
|  | void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Enable interrupt | ||||||
|  |  * @param gpio GpioPin | ||||||
|  |  */ | ||||||
|  | void hal_gpio_enable_int_callback(const GpioPin* gpio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Disable interrupt | ||||||
|  |  * @param gpio GpioPin | ||||||
|  |  */ | ||||||
|  | void hal_gpio_disable_int_callback(const GpioPin* gpio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Remove interrupt | ||||||
|  |  * @param gpio GpioPin | ||||||
|  |  */ | ||||||
|  | void hal_gpio_remove_int_callback(const GpioPin* gpio); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * GPIO write pin | ||||||
|  |  * @param gpio  GpioPin | ||||||
|  |  * @param state true / false | ||||||
|  |  */ | ||||||
|  | static inline void hal_gpio_write(const GpioPin* gpio, const bool state) { | ||||||
|  |     // writing to BSSR is an atomic operation
 | ||||||
|  |     if(state == true) { | ||||||
|  |         gpio->port->BSRR = gpio->pin; | ||||||
|  |     } else { | ||||||
|  |         gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * GPIO read pin | ||||||
|  |  * @param gpio GpioPin | ||||||
|  |  * @return true / false | ||||||
|  |  */ | ||||||
|  | static inline bool hal_gpio_read(const GpioPin* gpio) { | ||||||
|  |     if((gpio->port->IDR & gpio->pin) != 0x00U) { | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get RFID IN level | ||||||
|  |  * @return false = LOW, true = HIGH | ||||||
|  |  */ | ||||||
|  | bool hal_gpio_get_rfid_in_level(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										137
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | |||||||
|  | #include <furi-hal-i2c.h> | ||||||
|  | 
 | ||||||
|  | #include <stm32wbxx_ll_bus.h> | ||||||
|  | #include <stm32wbxx_ll_i2c.h> | ||||||
|  | #include <stm32wbxx_ll_rcc.h> | ||||||
|  | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | #include <stm32wbxx_ll_cortex.h> | ||||||
|  | 
 | ||||||
|  | void furi_hal_i2c_init() { | ||||||
|  |     LL_I2C_InitTypeDef I2C_InitStruct = {0}; | ||||||
|  |     LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; | ||||||
|  | 
 | ||||||
|  |     LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); | ||||||
|  | 
 | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||||
|  |     GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin; | ||||||
|  |     GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||||
|  |     GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; | ||||||
|  |     GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; | ||||||
|  |     GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; | ||||||
|  |     GPIO_InitStruct.Alternate = LL_GPIO_AF_4; | ||||||
|  |     LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||||
|  | 
 | ||||||
|  |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); | ||||||
|  | 
 | ||||||
|  |     I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; | ||||||
|  |     I2C_InitStruct.Timing = POWER_I2C_TIMINGS; | ||||||
|  |     I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; | ||||||
|  |     I2C_InitStruct.DigitalFilter = 0; | ||||||
|  |     I2C_InitStruct.OwnAddress1 = 0; | ||||||
|  |     I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK; | ||||||
|  |     I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT; | ||||||
|  |     LL_I2C_Init(I2C1, &I2C_InitStruct); | ||||||
|  |     LL_I2C_EnableAutoEndMode(I2C1); | ||||||
|  |     LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK); | ||||||
|  |     LL_I2C_DisableOwnAddress2(I2C1); | ||||||
|  |     LL_I2C_DisableGeneralCall(I2C1); | ||||||
|  |     LL_I2C_EnableClockStretching(I2C1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_tx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     uint8_t address, | ||||||
|  |     const uint8_t* data, | ||||||
|  |     uint8_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     uint32_t time_left = timeout; | ||||||
|  |     bool ret = true; | ||||||
|  | 
 | ||||||
|  |     while(LL_I2C_IsActiveFlag_BUSY(instance)) | ||||||
|  |         ; | ||||||
|  | 
 | ||||||
|  |     LL_I2C_HandleTransfer( | ||||||
|  |         instance, | ||||||
|  |         address, | ||||||
|  |         LL_I2C_ADDRSLAVE_7BIT, | ||||||
|  |         size, | ||||||
|  |         LL_I2C_MODE_AUTOEND, | ||||||
|  |         LL_I2C_GENERATE_START_WRITE); | ||||||
|  | 
 | ||||||
|  |     while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) { | ||||||
|  |         if(LL_I2C_IsActiveFlag_TXIS(instance)) { | ||||||
|  |             LL_I2C_TransmitData8(instance, (*data)); | ||||||
|  |             data++; | ||||||
|  |             size--; | ||||||
|  |             time_left = timeout; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(LL_SYSTICK_IsActiveCounterFlag()) { | ||||||
|  |             if(--time_left == 0) { | ||||||
|  |                 ret = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LL_I2C_ClearFlag_STOP(instance); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_rx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     uint8_t address, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint8_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     uint32_t time_left = timeout; | ||||||
|  |     bool ret = true; | ||||||
|  | 
 | ||||||
|  |     while(LL_I2C_IsActiveFlag_BUSY(instance)) | ||||||
|  |         ; | ||||||
|  | 
 | ||||||
|  |     LL_I2C_HandleTransfer( | ||||||
|  |         instance, | ||||||
|  |         address, | ||||||
|  |         LL_I2C_ADDRSLAVE_7BIT, | ||||||
|  |         size, | ||||||
|  |         LL_I2C_MODE_AUTOEND, | ||||||
|  |         LL_I2C_GENERATE_START_READ); | ||||||
|  | 
 | ||||||
|  |     while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) { | ||||||
|  |         if(LL_I2C_IsActiveFlag_RXNE(instance)) { | ||||||
|  |             *data = LL_I2C_ReceiveData8(instance); | ||||||
|  |             data++; | ||||||
|  |             size--; | ||||||
|  |             time_left = timeout; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(LL_SYSTICK_IsActiveCounterFlag()) { | ||||||
|  |             if(--time_left == 0) { | ||||||
|  |                 ret = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LL_I2C_ClearFlag_STOP(instance); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_trx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     uint8_t address, | ||||||
|  |     const uint8_t* tx_data, | ||||||
|  |     uint8_t tx_size, | ||||||
|  |     uint8_t* rx_data, | ||||||
|  |     uint8_t rx_size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) && | ||||||
|  |        furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) { | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								bootloader/targets/f7/furi-hal/furi-hal-i2c.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <furi-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void furi_hal_i2c_init(); | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_tx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     const uint8_t address, | ||||||
|  |     const uint8_t* data, | ||||||
|  |     const uint8_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_rx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     const uint8_t address, | ||||||
|  |     uint8_t* data, | ||||||
|  |     const uint8_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | bool furi_hal_i2c_trx( | ||||||
|  |     I2C_TypeDef* instance, | ||||||
|  |     const uint8_t address, | ||||||
|  |     const uint8_t* tx_data, | ||||||
|  |     const uint8_t tx_size, | ||||||
|  |     uint8_t* rx_data, | ||||||
|  |     const uint8_t rx_size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | #define with_furi_hal_i2c(type, pointer, function_body)       \ | ||||||
|  |     {                                                         \ | ||||||
|  |         *pointer = ({ type __fn__ function_body __fn__; })(); \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										43
									
								
								bootloader/targets/f7/furi-hal/furi-hal-light.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								bootloader/targets/f7/furi-hal/furi-hal-light.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | #include <furi-hal-light.h> | ||||||
|  | #include <lp5562.h> | ||||||
|  | 
 | ||||||
|  | #define LED_CURRENT_RED 50 | ||||||
|  | #define LED_CURRENT_GREEN 50 | ||||||
|  | #define LED_CURRENT_BLUE 50 | ||||||
|  | #define LED_CURRENT_WHITE 150 | ||||||
|  | 
 | ||||||
|  | void furi_hal_light_init() { | ||||||
|  |     lp5562_reset(); | ||||||
|  | 
 | ||||||
|  |     lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED); | ||||||
|  |     lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN); | ||||||
|  |     lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE); | ||||||
|  |     lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE); | ||||||
|  | 
 | ||||||
|  |     lp5562_set_channel_value(LP5562ChannelRed, 0x00); | ||||||
|  |     lp5562_set_channel_value(LP5562ChannelGreen, 0x00); | ||||||
|  |     lp5562_set_channel_value(LP5562ChannelBlue, 0x00); | ||||||
|  |     lp5562_set_channel_value(LP5562ChannelWhite, 0x00); | ||||||
|  | 
 | ||||||
|  |     lp5562_enable(); | ||||||
|  |     lp5562_configure(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_light_set(Light light, uint8_t value) { | ||||||
|  |     switch(light) { | ||||||
|  |     case LightRed: | ||||||
|  |         lp5562_set_channel_value(LP5562ChannelRed, value); | ||||||
|  |         break; | ||||||
|  |     case LightGreen: | ||||||
|  |         lp5562_set_channel_value(LP5562ChannelGreen, value); | ||||||
|  |         break; | ||||||
|  |     case LightBlue: | ||||||
|  |         lp5562_set_channel_value(LP5562ChannelBlue, value); | ||||||
|  |         break; | ||||||
|  |     case LightBacklight: | ||||||
|  |         lp5562_set_channel_value(LP5562ChannelWhite, value); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								bootloader/targets/f7/furi-hal/furi-hal-light.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								bootloader/targets/f7/furi-hal/furi-hal-light.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <furi-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void furi_hal_light_init(); | ||||||
|  | 
 | ||||||
|  | void furi_hal_light_set(Light light, uint8_t value); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										41
									
								
								bootloader/targets/f7/furi-hal/furi-hal-resources.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								bootloader/targets/f7/furi-hal/furi-hal-resources.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #include "furi-hal-resources.h" | ||||||
|  | #include "main.h" | ||||||
|  | 
 | ||||||
|  | const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; | ||||||
|  | const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; | ||||||
|  | const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; | ||||||
|  | const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; | ||||||
|  | const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; | ||||||
|  | const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; | ||||||
|  | const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; | ||||||
|  | const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin}; | ||||||
|  | const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin}; | ||||||
|  | const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin}; | ||||||
|  | const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; | ||||||
|  | const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; | ||||||
|  | const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; | ||||||
|  | const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; | ||||||
|  | const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; | ||||||
|  | const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; | ||||||
|  | const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; | ||||||
|  | const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; | ||||||
|  | const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; | ||||||
|  | const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||||
|  | const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||||
|  | const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; | ||||||
|  | const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; | ||||||
|  | 
 | ||||||
|  | const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin}; | ||||||
|  | const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin}; | ||||||
							
								
								
									
										82
									
								
								bootloader/targets/f7/furi-hal/furi-hal-resources.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								bootloader/targets/f7/furi-hal/furi-hal-resources.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stm32wbxx.h> | ||||||
|  | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | #include <furi-hal-gpio.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define POWER_I2C_SCL_Pin LL_GPIO_PIN_9 | ||||||
|  | #define POWER_I2C_SCL_GPIO_Port GPIOA | ||||||
|  | #define POWER_I2C_SDA_Pin LL_GPIO_PIN_10 | ||||||
|  | #define POWER_I2C_SDA_GPIO_Port GPIOA | ||||||
|  | 
 | ||||||
|  | #define POWER_I2C I2C1 | ||||||
|  | /* Timing register value is computed with the STM32CubeMX Tool,
 | ||||||
|  |   * Fast Mode @100kHz with I2CCLK = 64 MHz, | ||||||
|  |   * rise time = 0ns, fall time = 0ns | ||||||
|  |   */ | ||||||
|  | #define POWER_I2C_TIMINGS 0x10707DBC | ||||||
|  | 
 | ||||||
|  | /* Input Keys */ | ||||||
|  | typedef enum { | ||||||
|  |     InputKeyUp, | ||||||
|  |     InputKeyDown, | ||||||
|  |     InputKeyRight, | ||||||
|  |     InputKeyLeft, | ||||||
|  |     InputKeyOk, | ||||||
|  |     InputKeyBack, | ||||||
|  | } InputKey; | ||||||
|  | 
 | ||||||
|  | /* Light */ | ||||||
|  | typedef enum { | ||||||
|  |     LightRed, | ||||||
|  |     LightGreen, | ||||||
|  |     LightBlue, | ||||||
|  |     LightBacklight, | ||||||
|  | } Light; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin vibro_gpio; | ||||||
|  | extern const GpioPin ibutton_gpio; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_cc1101_g0; | ||||||
|  | extern const GpioPin gpio_rf_sw_0; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_subghz_cs; | ||||||
|  | extern const GpioPin gpio_display_cs; | ||||||
|  | extern const GpioPin gpio_display_rst; | ||||||
|  | extern const GpioPin gpio_display_di; | ||||||
|  | extern const GpioPin gpio_sdcard_cs; | ||||||
|  | extern const GpioPin gpio_nfc_cs; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_spi_d_miso; | ||||||
|  | extern const GpioPin gpio_spi_d_mosi; | ||||||
|  | extern const GpioPin gpio_spi_d_sck; | ||||||
|  | extern const GpioPin gpio_spi_r_miso; | ||||||
|  | extern const GpioPin gpio_spi_r_mosi; | ||||||
|  | extern const GpioPin gpio_spi_r_sck; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_ext_pc0; | ||||||
|  | extern const GpioPin gpio_ext_pc1; | ||||||
|  | extern const GpioPin gpio_ext_pc3; | ||||||
|  | extern const GpioPin gpio_ext_pb2; | ||||||
|  | extern const GpioPin gpio_ext_pb3; | ||||||
|  | extern const GpioPin gpio_ext_pa4; | ||||||
|  | extern const GpioPin gpio_ext_pa6; | ||||||
|  | extern const GpioPin gpio_ext_pa7; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_rfid_pull; | ||||||
|  | extern const GpioPin gpio_rfid_carrier_out; | ||||||
|  | extern const GpioPin gpio_rfid_data_in; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_irda_rx; | ||||||
|  | extern const GpioPin gpio_irda_tx; | ||||||
|  | 
 | ||||||
|  | extern const GpioPin gpio_usart_tx; | ||||||
|  | extern const GpioPin gpio_usart_rx; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										114
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi-config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi-config.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | |||||||
|  | #include <furi-hal-spi-config.h> | ||||||
|  | #include <furi-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | #define SPI_R SPI1 | ||||||
|  | #define SPI_D SPI2 | ||||||
|  | 
 | ||||||
|  | const LL_SPI_InitTypeDef furi_hal_spi_config_nfc = { | ||||||
|  |     .Mode = LL_SPI_MODE_MASTER, | ||||||
|  |     .TransferDirection = LL_SPI_FULL_DUPLEX, | ||||||
|  |     .DataWidth = LL_SPI_DATAWIDTH_8BIT, | ||||||
|  |     .ClockPolarity = LL_SPI_POLARITY_LOW, | ||||||
|  |     .ClockPhase = LL_SPI_PHASE_2EDGE, | ||||||
|  |     .NSS = LL_SPI_NSS_SOFT, | ||||||
|  |     .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, | ||||||
|  |     .BitOrder = LL_SPI_MSB_FIRST, | ||||||
|  |     .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, | ||||||
|  |     .CRCPoly = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const LL_SPI_InitTypeDef furi_hal_spi_config_subghz = { | ||||||
|  |     .Mode = LL_SPI_MODE_MASTER, | ||||||
|  |     .TransferDirection = LL_SPI_FULL_DUPLEX, | ||||||
|  |     .DataWidth = LL_SPI_DATAWIDTH_8BIT, | ||||||
|  |     .ClockPolarity = LL_SPI_POLARITY_LOW, | ||||||
|  |     .ClockPhase = LL_SPI_PHASE_1EDGE, | ||||||
|  |     .NSS = LL_SPI_NSS_SOFT, | ||||||
|  |     .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, | ||||||
|  |     .BitOrder = LL_SPI_MSB_FIRST, | ||||||
|  |     .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, | ||||||
|  |     .CRCPoly = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const LL_SPI_InitTypeDef furi_hal_spi_config_display = { | ||||||
|  |     .Mode = LL_SPI_MODE_MASTER, | ||||||
|  |     .TransferDirection = LL_SPI_FULL_DUPLEX, | ||||||
|  |     .DataWidth = LL_SPI_DATAWIDTH_8BIT, | ||||||
|  |     .ClockPolarity = LL_SPI_POLARITY_LOW, | ||||||
|  |     .ClockPhase = LL_SPI_PHASE_1EDGE, | ||||||
|  |     .NSS = LL_SPI_NSS_SOFT, | ||||||
|  |     .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, | ||||||
|  |     .BitOrder = LL_SPI_MSB_FIRST, | ||||||
|  |     .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, | ||||||
|  |     .CRCPoly = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * SD Card in fast mode (after init) | ||||||
|  |  */ | ||||||
|  | const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast = { | ||||||
|  |     .Mode = LL_SPI_MODE_MASTER, | ||||||
|  |     .TransferDirection = LL_SPI_FULL_DUPLEX, | ||||||
|  |     .DataWidth = LL_SPI_DATAWIDTH_8BIT, | ||||||
|  |     .ClockPolarity = LL_SPI_POLARITY_LOW, | ||||||
|  |     .ClockPhase = LL_SPI_PHASE_1EDGE, | ||||||
|  |     .NSS = LL_SPI_NSS_SOFT, | ||||||
|  |     .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, | ||||||
|  |     .BitOrder = LL_SPI_MSB_FIRST, | ||||||
|  |     .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, | ||||||
|  |     .CRCPoly = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * SD Card in slow mode (before init) | ||||||
|  |  */ | ||||||
|  | const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow = { | ||||||
|  |     .Mode = LL_SPI_MODE_MASTER, | ||||||
|  |     .TransferDirection = LL_SPI_FULL_DUPLEX, | ||||||
|  |     .DataWidth = LL_SPI_DATAWIDTH_8BIT, | ||||||
|  |     .ClockPolarity = LL_SPI_POLARITY_LOW, | ||||||
|  |     .ClockPhase = LL_SPI_PHASE_1EDGE, | ||||||
|  |     .NSS = LL_SPI_NSS_SOFT, | ||||||
|  |     .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, | ||||||
|  |     .BitOrder = LL_SPI_MSB_FIRST, | ||||||
|  |     .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, | ||||||
|  |     .CRCPoly = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const FuriHalSpiBus spi_r = { | ||||||
|  |     .spi = SPI_R, | ||||||
|  |     .miso = &gpio_spi_r_miso, | ||||||
|  |     .mosi = &gpio_spi_r_mosi, | ||||||
|  |     .clk = &gpio_spi_r_sck, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const FuriHalSpiBus spi_d = { | ||||||
|  |     .spi = SPI_D, | ||||||
|  |     .miso = &gpio_spi_d_miso, | ||||||
|  |     .mosi = &gpio_spi_d_mosi, | ||||||
|  |     .clk = &gpio_spi_d_sck, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax] = { | ||||||
|  |     { | ||||||
|  |         .bus = &spi_r, | ||||||
|  |         .config = &furi_hal_spi_config_subghz, | ||||||
|  |         .chip_select = &gpio_subghz_cs, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .bus = &spi_d, | ||||||
|  |         .config = &furi_hal_spi_config_display, | ||||||
|  |         .chip_select = &gpio_display_cs, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .bus = &spi_d, | ||||||
|  |         .config = &furi_hal_spi_config_sd_fast, | ||||||
|  |         .chip_select = &gpio_sdcard_cs, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         .bus = &spi_d, | ||||||
|  |         .config = &furi_hal_spi_config_sd_slow, | ||||||
|  |         .chip_select = &gpio_sdcard_cs, | ||||||
|  |     }, | ||||||
|  |     {.bus = &spi_r, .config = &furi_hal_spi_config_nfc, .chip_select = &gpio_nfc_cs}, | ||||||
|  | }; | ||||||
							
								
								
									
										61
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi-config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi-config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi-hal-gpio.h> | ||||||
|  | #include <stm32wbxx_ll_spi.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | extern const LL_SPI_InitTypeDef furi_hal_spi_config_nfc; | ||||||
|  | extern const LL_SPI_InitTypeDef furi_hal_spi_config_subghz; | ||||||
|  | extern const LL_SPI_InitTypeDef furi_hal_spi_config_display; | ||||||
|  | extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast; | ||||||
|  | extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow; | ||||||
|  | 
 | ||||||
|  | /** FURI HAL SPI BUS handler
 | ||||||
|  |  * Structure content may change at some point | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     const SPI_TypeDef* spi; | ||||||
|  |     const GpioPin* miso; | ||||||
|  |     const GpioPin* mosi; | ||||||
|  |     const GpioPin* clk; | ||||||
|  | } FuriHalSpiBus; | ||||||
|  | 
 | ||||||
|  | /** FURI HAL SPI Device handler
 | ||||||
|  |  * Structure content may change at some point | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     const FuriHalSpiBus* bus; | ||||||
|  |     const LL_SPI_InitTypeDef* config; | ||||||
|  |     const GpioPin* chip_select; | ||||||
|  | } FuriHalSpiDevice; | ||||||
|  | 
 | ||||||
|  | /** FURI HAL SPI Standard Device IDs */ | ||||||
|  | typedef enum { | ||||||
|  |     FuriHalSpiDeviceIdSubGhz, /** SubGhz: CC1101, non-standard SPI usage */ | ||||||
|  |     FuriHalSpiDeviceIdDisplay, /** Display: ERC12864, only have MOSI */ | ||||||
|  |     FuriHalSpiDeviceIdSdCardFast, /** SDCARD: fast mode, after initialization */ | ||||||
|  |     FuriHalSpiDeviceIdSdCardSlow, /** SDCARD: slow mode, before initialization */ | ||||||
|  |     FuriHalSpiDeviceIdNfc, /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */ | ||||||
|  | 
 | ||||||
|  |     FuriHalSpiDeviceIdMax, /** Service Value, do not use */ | ||||||
|  | } FuriHalSpiDeviceId; | ||||||
|  | 
 | ||||||
|  | /** Furi Hal Spi Bus R
 | ||||||
|  |  * CC1101, Nfc | ||||||
|  |  */ | ||||||
|  | extern const FuriHalSpiBus spi_r; | ||||||
|  | 
 | ||||||
|  | /** Furi Hal Spi Bus D
 | ||||||
|  |  * Display, SdCard | ||||||
|  |  */ | ||||||
|  | extern const FuriHalSpiBus spi_d; | ||||||
|  | 
 | ||||||
|  | /** Furi Hal Spi devices */ | ||||||
|  | extern const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax]; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										240
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,240 @@ | |||||||
|  | #include "furi-hal-spi.h" | ||||||
|  | #include "furi-hal-resources.h" | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #include <stm32wbxx_ll_spi.h> | ||||||
|  | #include <stm32wbxx_ll_utils.h> | ||||||
|  | #include <stm32wbxx_ll_cortex.h> | ||||||
|  | 
 | ||||||
|  | extern void Enable_SPI(SPI_TypeDef* spi); | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_init() { | ||||||
|  |     for(size_t i = 0; i < FuriHalSpiDeviceIdMax; ++i) { | ||||||
|  |         hal_gpio_write(furi_hal_spi_devices[i].chip_select, true); | ||||||
|  |         hal_gpio_init( | ||||||
|  |             furi_hal_spi_devices[i].chip_select, | ||||||
|  |             GpioModeOutputPushPull, | ||||||
|  |             GpioPullNo, | ||||||
|  |             GpioSpeedVeryHigh); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_r_miso, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI1); | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_r_mosi, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI1); | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_r_sck, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullNo, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI1); | ||||||
|  | 
 | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_d_miso, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_d_mosi, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  |     hal_gpio_init_ex( | ||||||
|  |         &gpio_spi_d_sck, | ||||||
|  |         GpioModeAltFunctionPushPull, | ||||||
|  |         GpioPullUp, | ||||||
|  |         GpioSpeedVeryHigh, | ||||||
|  |         GpioAltFn5SPI2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus) { | ||||||
|  |     assert(bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus) { | ||||||
|  |     assert(bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config) { | ||||||
|  |     assert(bus); | ||||||
|  |     LL_SPI_DeInit((SPI_TypeDef*)bus->spi); | ||||||
|  |     LL_SPI_Init((SPI_TypeDef*)bus->spi, (LL_SPI_InitTypeDef*)config); | ||||||
|  |     LL_SPI_SetRxFIFOThreshold((SPI_TypeDef*)bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); | ||||||
|  |     LL_SPI_Enable((SPI_TypeDef*)bus->spi); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_bus_end_txrx(const FuriHalSpiBus* bus, uint32_t timeout) { | ||||||
|  |     while(LL_SPI_GetTxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_TX_FIFO_EMPTY) | ||||||
|  |         ; | ||||||
|  |     while(LL_SPI_IsActiveFlag_BSY((SPI_TypeDef*)bus->spi)) | ||||||
|  |         ; | ||||||
|  |     while(LL_SPI_GetRxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_RX_FIFO_EMPTY) { | ||||||
|  |         LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) { | ||||||
|  |     assert(bus); | ||||||
|  |     assert(buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  | 
 | ||||||
|  |     return furi_hal_spi_bus_trx(bus, buffer, buffer, size, timeout); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) { | ||||||
|  |     assert(bus); | ||||||
|  |     assert(buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  |     bool ret = true; | ||||||
|  | 
 | ||||||
|  |     while(size > 0) { | ||||||
|  |         if(LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi)) { | ||||||
|  |             LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *buffer); | ||||||
|  |             buffer++; | ||||||
|  |             size--; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_bus_end_txrx(bus, timeout); | ||||||
|  |     LL_SPI_ClearFlag_OVR((SPI_TypeDef*)bus->spi); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_bus_trx( | ||||||
|  |     const FuriHalSpiBus* bus, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     assert(bus); | ||||||
|  |     assert(tx_buffer); | ||||||
|  |     assert(rx_buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  |     bool ret = true; | ||||||
|  |     size_t tx_size = size; | ||||||
|  |     bool tx_allowed = true; | ||||||
|  | 
 | ||||||
|  |     while(size > 0) { | ||||||
|  |         if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi) && tx_allowed) { | ||||||
|  |             LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *tx_buffer); | ||||||
|  |             tx_buffer++; | ||||||
|  |             tx_size--; | ||||||
|  |             tx_allowed = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(LL_SPI_IsActiveFlag_RXNE((SPI_TypeDef*)bus->spi)) { | ||||||
|  |             *rx_buffer = LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi); | ||||||
|  |             rx_buffer++; | ||||||
|  |             size--; | ||||||
|  |             tx_allowed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_bus_end_txrx(bus, timeout); | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_device_configure(const FuriHalSpiDevice* device) { | ||||||
|  |     assert(device); | ||||||
|  |     assert(device->config); | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_bus_configure(device->bus, device->config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) { | ||||||
|  |     assert(device_id < FuriHalSpiDeviceIdMax); | ||||||
|  | 
 | ||||||
|  |     const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id]; | ||||||
|  |     assert(device); | ||||||
|  | 
 | ||||||
|  |     furi_hal_spi_bus_lock(device->bus); | ||||||
|  |     furi_hal_spi_device_configure(device); | ||||||
|  | 
 | ||||||
|  |     return device; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_spi_device_return(const FuriHalSpiDevice* device) { | ||||||
|  |     furi_hal_spi_bus_unlock(device->bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_device_rx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     assert(device); | ||||||
|  |     assert(buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool ret = furi_hal_spi_bus_rx(device->bus, buffer, size, timeout); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_device_tx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     assert(device); | ||||||
|  |     assert(buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool ret = furi_hal_spi_bus_tx(device->bus, buffer, size, timeout); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_spi_device_trx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout) { | ||||||
|  |     assert(device); | ||||||
|  |     assert(tx_buffer); | ||||||
|  |     assert(rx_buffer); | ||||||
|  |     assert(size > 0); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool ret = furi_hal_spi_bus_trx(device->bus, tx_buffer, rx_buffer, size, timeout); | ||||||
|  | 
 | ||||||
|  |     if(device->chip_select) { | ||||||
|  |         hal_gpio_write(device->chip_select, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								bootloader/targets/f7/furi-hal/furi-hal-spi.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "main.h" | ||||||
|  | 
 | ||||||
|  | #include "furi-hal-spi-config.h" | ||||||
|  | #include <furi-hal-gpio.h> | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Init SPI API | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_init(); | ||||||
|  | 
 | ||||||
|  | /* Bus Level API */ | ||||||
|  | 
 | ||||||
|  | /** Lock SPI bus
 | ||||||
|  |  * Takes bus mutex, if used | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus); | ||||||
|  | 
 | ||||||
|  | /** Unlock SPI bus
 | ||||||
|  |  * Releases BUS mutex, if used | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Configure SPI bus | ||||||
|  |  * @param bus - spi bus handler | ||||||
|  |  * @param config - spi configuration structure | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config); | ||||||
|  | 
 | ||||||
|  | /** SPI Receive
 | ||||||
|  |  * @param bus - spi bus handler | ||||||
|  |  * @param buffer - receive buffer | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /** SPI Transmit
 | ||||||
|  |  * @param bus - spi bus handler | ||||||
|  |  * @param buffer - transmit buffer | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /** SPI Transmit and Receive
 | ||||||
|  |  * @param bus - spi bus handlere | ||||||
|  |  * @param tx_buffer - device handle | ||||||
|  |  * @param rx_buffer - device handle | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_bus_trx( | ||||||
|  |     const FuriHalSpiBus* bus, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /* Device Level API */ | ||||||
|  | 
 | ||||||
|  | /** Reconfigure SPI bus for device
 | ||||||
|  |  * @param device - device description | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_device_configure(const FuriHalSpiDevice* device); | ||||||
|  | 
 | ||||||
|  | /** Get Device handle
 | ||||||
|  |  * And lock access to the corresponding SPI BUS | ||||||
|  |  * @param device_id - device identifier | ||||||
|  |  * @return device handle | ||||||
|  |  */ | ||||||
|  | const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id); | ||||||
|  | 
 | ||||||
|  | /** Return Device handle
 | ||||||
|  |  * And unlock access to the corresponding SPI BUS | ||||||
|  |  * @param device - device handle | ||||||
|  |  */ | ||||||
|  | void furi_hal_spi_device_return(const FuriHalSpiDevice* device); | ||||||
|  | 
 | ||||||
|  | /** SPI Recieve
 | ||||||
|  |  * @param device - device handle | ||||||
|  |  * @param buffer - receive buffer | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_device_rx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /** SPI Transmit
 | ||||||
|  |  * @param device - device handle | ||||||
|  |  * @param buffer - transmit buffer | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_device_tx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /** SPI Transmit and Receive
 | ||||||
|  |  * @param device - device handle | ||||||
|  |  * @param tx_buffer - device handle | ||||||
|  |  * @param rx_buffer - device handle | ||||||
|  |  * @param size - transaction size | ||||||
|  |  * @param timeout - bus operation timeout in ms | ||||||
|  |  */ | ||||||
|  | bool furi_hal_spi_device_trx( | ||||||
|  |     const FuriHalSpiDevice* device, | ||||||
|  |     uint8_t* tx_buffer, | ||||||
|  |     uint8_t* rx_buffer, | ||||||
|  |     size_t size, | ||||||
|  |     uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										7
									
								
								bootloader/targets/f7/furi-hal/furi-hal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								bootloader/targets/f7/furi-hal/furi-hal.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | #include <furi-hal.h> | ||||||
|  | 
 | ||||||
|  | void furi_hal_init() { | ||||||
|  |     furi_hal_i2c_init(); | ||||||
|  |     furi_hal_light_init(); | ||||||
|  |     furi_hal_spi_init(); | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								bootloader/targets/f7/furi-hal/furi-hal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								bootloader/targets/f7/furi-hal/furi-hal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi-hal-i2c.h> | ||||||
|  | #include <furi-hal-light.h> | ||||||
|  | #include <furi-hal-resources.h> | ||||||
|  | #include <furi-hal-spi.h> | ||||||
|  | 
 | ||||||
|  | void furi_hal_init(); | ||||||
							
								
								
									
										108
									
								
								bootloader/targets/f7/furi-hal/main.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								bootloader/targets/f7/furi-hal/main.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stm32wbxx.h> | ||||||
|  | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | #include <stm32wbxx_ll_spi.h> | ||||||
|  | 
 | ||||||
|  | #define BUTTON_BACK_GPIO_Port GPIOC | ||||||
|  | #define BUTTON_BACK_Pin LL_GPIO_PIN_13 | ||||||
|  | #define BUTTON_DOWN_GPIO_Port GPIOC | ||||||
|  | #define BUTTON_DOWN_Pin LL_GPIO_PIN_6 | ||||||
|  | #define BUTTON_LEFT_GPIO_Port GPIOB | ||||||
|  | #define BUTTON_LEFT_Pin LL_GPIO_PIN_11 | ||||||
|  | #define BUTTON_OK_GPIO_Port GPIOH | ||||||
|  | #define BUTTON_OK_Pin LL_GPIO_PIN_3 | ||||||
|  | #define BUTTON_RIGHT_GPIO_Port GPIOB | ||||||
|  | #define BUTTON_RIGHT_Pin LL_GPIO_PIN_12 | ||||||
|  | #define BUTTON_UP_GPIO_Port GPIOB | ||||||
|  | #define BUTTON_UP_Pin LL_GPIO_PIN_10 | ||||||
|  | 
 | ||||||
|  | #define CC1101_CS_GPIO_Port GPIOD | ||||||
|  | #define CC1101_CS_Pin LL_GPIO_PIN_0 | ||||||
|  | #define CC1101_G0_GPIO_Port GPIOA | ||||||
|  | #define CC1101_G0_Pin LL_GPIO_PIN_1 | ||||||
|  | 
 | ||||||
|  | #define DISPLAY_CS_GPIO_Port GPIOC | ||||||
|  | #define DISPLAY_CS_Pin LL_GPIO_PIN_11 | ||||||
|  | #define DISPLAY_DI_GPIO_Port GPIOB | ||||||
|  | #define DISPLAY_DI_Pin LL_GPIO_PIN_1 | ||||||
|  | #define DISPLAY_RST_GPIO_Port GPIOB | ||||||
|  | #define DISPLAY_RST_Pin LL_GPIO_PIN_0 | ||||||
|  | 
 | ||||||
|  | #define IR_RX_GPIO_Port GPIOA | ||||||
|  | #define IR_RX_Pin LL_GPIO_PIN_0 | ||||||
|  | #define IR_TX_GPIO_Port GPIOB | ||||||
|  | #define IR_TX_Pin LL_GPIO_PIN_9 | ||||||
|  | 
 | ||||||
|  | #define NFC_CS_GPIO_Port GPIOE | ||||||
|  | #define NFC_CS_Pin LL_GPIO_PIN_4 | ||||||
|  | 
 | ||||||
|  | #define PA4_GPIO_Port GPIOA | ||||||
|  | #define PA4_Pin LL_GPIO_PIN_4 | ||||||
|  | #define PA6_GPIO_Port GPIOA | ||||||
|  | #define PA6_Pin LL_GPIO_PIN_6 | ||||||
|  | #define PA7_GPIO_Port GPIOA | ||||||
|  | #define PA7_Pin LL_GPIO_PIN_7 | ||||||
|  | #define PB2_GPIO_Port GPIOB | ||||||
|  | #define PB2_Pin LL_GPIO_PIN_2 | ||||||
|  | #define PB3_GPIO_Port GPIOB | ||||||
|  | #define PB3_Pin LL_GPIO_PIN_3 | ||||||
|  | #define PC0_GPIO_Port GPIOC | ||||||
|  | #define PC0_Pin LL_GPIO_PIN_0 | ||||||
|  | #define PC1_GPIO_Port GPIOC | ||||||
|  | #define PC1_Pin LL_GPIO_PIN_1 | ||||||
|  | #define PC3_GPIO_Port GPIOC | ||||||
|  | #define PC3_Pin LL_GPIO_PIN_3 | ||||||
|  | 
 | ||||||
|  | #define PERIPH_POWER_GPIO_Port GPIOA | ||||||
|  | #define PERIPH_POWER_Pin LL_GPIO_PIN_3 | ||||||
|  | 
 | ||||||
|  | #define QUARTZ_32MHZ_IN_GPIO_Port GPIOC | ||||||
|  | #define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14 | ||||||
|  | #define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC | ||||||
|  | #define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15 | ||||||
|  | 
 | ||||||
|  | #define RFID_OUT_GPIO_Port GPIOB | ||||||
|  | #define RFID_OUT_Pin LL_GPIO_PIN_13 | ||||||
|  | #define RFID_PULL_GPIO_Port GPIOA | ||||||
|  | #define RFID_PULL_Pin LL_GPIO_PIN_2 | ||||||
|  | #define RFID_RF_IN_GPIO_Port GPIOC | ||||||
|  | #define RFID_RF_IN_Pin LL_GPIO_PIN_5 | ||||||
|  | #define RFID_TUNE_GPIO_Port GPIOA | ||||||
|  | #define RFID_TUNE_Pin LL_GPIO_PIN_8 | ||||||
|  | 
 | ||||||
|  | #define RF_SW_0_GPIO_Port GPIOC | ||||||
|  | #define RF_SW_0_Pin LL_GPIO_PIN_4 | ||||||
|  | 
 | ||||||
|  | #define SD_CD_GPIO_Port GPIOC | ||||||
|  | #define SD_CD_Pin LL_GPIO_PIN_10 | ||||||
|  | #define SD_CS_GPIO_Port GPIOC | ||||||
|  | #define SD_CS_Pin LL_GPIO_PIN_12 | ||||||
|  | 
 | ||||||
|  | #define SPEAKER_GPIO_Port GPIOB | ||||||
|  | #define SPEAKER_Pin LL_GPIO_PIN_8 | ||||||
|  | 
 | ||||||
|  | #define VIBRO_GPIO_Port GPIOA | ||||||
|  | #define VIBRO_Pin LL_GPIO_PIN_15 | ||||||
|  | 
 | ||||||
|  | #define iBTN_GPIO_Port GPIOB | ||||||
|  | #define iBTN_Pin LL_GPIO_PIN_14 | ||||||
|  | 
 | ||||||
|  | #define USART1_TX_Pin LL_GPIO_PIN_6 | ||||||
|  | #define USART1_TX_Port GPIOB | ||||||
|  | #define USART1_RX_Pin LL_GPIO_PIN_7 | ||||||
|  | #define USART1_RX_Port GPIOB | ||||||
|  | 
 | ||||||
|  | #define SPI_D_MISO_GPIO_Port GPIOC | ||||||
|  | #define SPI_D_MISO_Pin LL_GPIO_PIN_2 | ||||||
|  | #define SPI_D_MOSI_GPIO_Port GPIOB | ||||||
|  | #define SPI_D_MOSI_Pin LL_GPIO_PIN_15 | ||||||
|  | #define SPI_D_SCK_GPIO_Port GPIOD | ||||||
|  | #define SPI_D_SCK_Pin LL_GPIO_PIN_1 | ||||||
|  | 
 | ||||||
|  | #define SPI_R_MISO_GPIO_Port GPIOB | ||||||
|  | #define SPI_R_MISO_Pin LL_GPIO_PIN_4 | ||||||
|  | #define SPI_R_MOSI_GPIO_Port GPIOB | ||||||
|  | #define SPI_R_MOSI_Pin LL_GPIO_PIN_5 | ||||||
|  | #define SPI_R_SCK_GPIO_Port GPIOA | ||||||
|  | #define SPI_R_SCK_Pin LL_GPIO_PIN_5 | ||||||
							
								
								
									
										69
									
								
								bootloader/targets/f7/furi-hal/u8g2_periphery.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								bootloader/targets/f7/furi-hal/u8g2_periphery.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | #include <u8g2.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <stm32wbxx_ll_utils.h> | ||||||
|  | 
 | ||||||
|  | static FuriHalSpiDevice* u8g2_periphery_display = NULL; | ||||||
|  | 
 | ||||||
|  | uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { | ||||||
|  |     switch(msg) { | ||||||
|  |     case U8X8_MSG_GPIO_AND_DELAY_INIT: | ||||||
|  |         /* HAL initialization contains all what we need so we can skip this part. */ | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_DELAY_MILLI: | ||||||
|  |         LL_mDelay(arg_int); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_DELAY_10MICRO: | ||||||
|  |         LL_mDelay(1); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_DELAY_100NANO: | ||||||
|  |         asm("nop"); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_GPIO_RESET: | ||||||
|  |         hal_gpio_write(&gpio_display_rst, arg_int); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { | ||||||
|  |     switch(msg) { | ||||||
|  |     case U8X8_MSG_BYTE_SEND: | ||||||
|  |         furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_BYTE_SET_DC: | ||||||
|  |         hal_gpio_write(&gpio_display_di, arg_int); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_BYTE_INIT: | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_BYTE_START_TRANSFER: | ||||||
|  |         assert(u8g2_periphery_display == NULL); | ||||||
|  |         u8g2_periphery_display = | ||||||
|  |             (FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay); | ||||||
|  |         hal_gpio_write(u8g2_periphery_display->chip_select, false); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case U8X8_MSG_BYTE_END_TRANSFER: | ||||||
|  |         assert(u8g2_periphery_display); | ||||||
|  |         hal_gpio_write(u8g2_periphery_display->chip_select, true); | ||||||
|  |         furi_hal_spi_device_return(u8g2_periphery_display); | ||||||
|  |         u8g2_periphery_display = NULL; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
							
								
								
									
										187
									
								
								bootloader/targets/f7/stm32wb55xx_flash_cm4.ld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								bootloader/targets/f7/stm32wb55xx_flash_cm4.ld
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | |||||||
|  | /** | ||||||
|  | ***************************************************************************** | ||||||
|  | ** | ||||||
|  | **  File        : stm32wb55xx_flash_cm4.ld | ||||||
|  | ** | ||||||
|  | **  Abstract    : System Workbench Minimal System calls file | ||||||
|  | ** | ||||||
|  | ** 		          For more information about which c-functions | ||||||
|  | **                need which of these lowlevel functions | ||||||
|  | **                please consult the Newlib libc-manual | ||||||
|  | ** | ||||||
|  | **  Environment : System Workbench for MCU | ||||||
|  | ** | ||||||
|  | **  Distribution: The file is distributed “as is,” without any warranty | ||||||
|  | **                of any kind. | ||||||
|  | ** | ||||||
|  | ***************************************************************************** | ||||||
|  | ** | ||||||
|  | ** <h2><center>© COPYRIGHT(c) 2019 Ac6</center></h2> | ||||||
|  | ** | ||||||
|  | ** Redistribution and use in source and binary forms, with or without modification, | ||||||
|  | ** are permitted provided that the following conditions are met: | ||||||
|  | **   1. Redistributions of source code must retain the above copyright notice, | ||||||
|  | **      this list of conditions and the following disclaimer. | ||||||
|  | **   2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  | **      this list of conditions and the following disclaimer in the documentation | ||||||
|  | **      and/or other materials provided with the distribution. | ||||||
|  | **   3. Neither the name of Ac6 nor the names of its contributors | ||||||
|  | **      may be used to endorse or promote products derived from this software | ||||||
|  | **      without specific prior written permission. | ||||||
|  | ** | ||||||
|  | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  | ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  | ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  | ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  | ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | ** | ||||||
|  | ***************************************************************************** | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /* Entry Point */ | ||||||
|  | ENTRY(Reset_Handler) | ||||||
|  | 
 | ||||||
|  | /* Highest address of the user mode stack */ | ||||||
|  | _estack = 0x20030000;    /* end of RAM */ | ||||||
|  | /* Generate a link error if heap and stack don't fit into RAM */ | ||||||
|  | _Min_Heap_Size = 0x200;      /* required amount of heap  */ | ||||||
|  | _Min_Stack_Size = 0x400; /* required amount of stack */ | ||||||
|  | 
 | ||||||
|  | /* Specify the memory areas */ | ||||||
|  | MEMORY | ||||||
|  | { | ||||||
|  | FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 32K | ||||||
|  | RAM1 (xrw)                 : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 | ||||||
|  | RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Define output sections */ | ||||||
|  | SECTIONS | ||||||
|  | { | ||||||
|  |   /* The startup code goes first into FLASH */ | ||||||
|  |   .isr_vector : | ||||||
|  |   { | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     KEEP(*(.isr_vector)) /* Startup code */ | ||||||
|  |     . = ALIGN(4); | ||||||
|  |   } >FLASH | ||||||
|  | 
 | ||||||
|  |   /* The program code and other data goes into FLASH */ | ||||||
|  |   .text : | ||||||
|  |   { | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     *(.text)           /* .text sections (code) */ | ||||||
|  |     *(.text*)          /* .text* sections (code) */ | ||||||
|  |     *(.glue_7)         /* glue arm to thumb code */ | ||||||
|  |     *(.glue_7t)        /* glue thumb to arm code */ | ||||||
|  |     *(.eh_frame) | ||||||
|  | 
 | ||||||
|  |     KEEP (*(.init)) | ||||||
|  |     KEEP (*(.fini)) | ||||||
|  | 
 | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     _etext = .;        /* define a global symbols at end of code */ | ||||||
|  |   } >FLASH | ||||||
|  | 
 | ||||||
|  |   /* Constant data goes into FLASH */ | ||||||
|  |   .rodata : | ||||||
|  |   { | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     *(.rodata)         /* .rodata sections (constants, strings, etc.) */ | ||||||
|  |     *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */ | ||||||
|  |     . = ALIGN(4); | ||||||
|  |   } >FLASH | ||||||
|  | 
 | ||||||
|  |   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH | ||||||
|  |   .ARM : { | ||||||
|  |     __exidx_start = .; | ||||||
|  |     *(.ARM.exidx*) | ||||||
|  |     __exidx_end = .; | ||||||
|  |   } >FLASH | ||||||
|  | 
 | ||||||
|  |   .preinit_array     : | ||||||
|  |   { | ||||||
|  |     PROVIDE_HIDDEN (__preinit_array_start = .); | ||||||
|  |     KEEP (*(.preinit_array*)) | ||||||
|  |     PROVIDE_HIDDEN (__preinit_array_end = .); | ||||||
|  |   } >FLASH | ||||||
|  |   .init_array : | ||||||
|  |   { | ||||||
|  |     PROVIDE_HIDDEN (__init_array_start = .); | ||||||
|  |     KEEP (*(SORT(.init_array.*))) | ||||||
|  |     KEEP (*(.init_array*)) | ||||||
|  |     PROVIDE_HIDDEN (__init_array_end = .); | ||||||
|  |   } >FLASH | ||||||
|  |   .fini_array : | ||||||
|  |   { | ||||||
|  |     PROVIDE_HIDDEN (__fini_array_start = .); | ||||||
|  |     KEEP (*(SORT(.fini_array.*))) | ||||||
|  |     KEEP (*(.fini_array*)) | ||||||
|  |     PROVIDE_HIDDEN (__fini_array_end = .); | ||||||
|  |   } >FLASH | ||||||
|  | 
 | ||||||
|  |   /* used by the startup to initialize data */ | ||||||
|  |   _sidata = LOADADDR(.data); | ||||||
|  | 
 | ||||||
|  |   /* Initialized data sections goes into RAM, load LMA copy after code */ | ||||||
|  |   .data :  | ||||||
|  |   { | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     _sdata = .;        /* create a global symbol at data start */ | ||||||
|  |     *(.data)           /* .data sections */ | ||||||
|  |     *(.data*)          /* .data* sections */ | ||||||
|  | 
 | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     _edata = .;        /* define a global symbol at data end */ | ||||||
|  |   } >RAM1 AT> FLASH | ||||||
|  | 
 | ||||||
|  |    | ||||||
|  |   /* Uninitialized data section */ | ||||||
|  |   . = ALIGN(4); | ||||||
|  |   .bss : | ||||||
|  |   { | ||||||
|  |     /* This is used by the startup in order to initialize the .bss secion */ | ||||||
|  |     _sbss = .;         /* define a global symbol at bss start */ | ||||||
|  |     __bss_start__ = _sbss; | ||||||
|  |     *(.bss) | ||||||
|  |     *(.bss*) | ||||||
|  |     *(COMMON) | ||||||
|  | 
 | ||||||
|  |     . = ALIGN(4); | ||||||
|  |     _ebss = .;         /* define a global symbol at bss end */ | ||||||
|  |     __bss_end__ = _ebss; | ||||||
|  |   } >RAM1 | ||||||
|  | 
 | ||||||
|  |   /* User_heap_stack section, used to check that there is enough RAM left */ | ||||||
|  |   ._user_heap_stack : | ||||||
|  |   { | ||||||
|  |     . = ALIGN(8); | ||||||
|  |     PROVIDE ( end = . ); | ||||||
|  |     PROVIDE ( _end = . ); | ||||||
|  |     . = . + _Min_Heap_Size; | ||||||
|  |     . = . + _Min_Stack_Size; | ||||||
|  |     . = ALIGN(8); | ||||||
|  |   } >RAM1 | ||||||
|  | 
 | ||||||
|  |    | ||||||
|  | 
 | ||||||
|  |   /* Remove information from the standard libraries */ | ||||||
|  |   /DISCARD/ : | ||||||
|  |   { | ||||||
|  |     libc.a ( * ) | ||||||
|  |     libm.a ( * ) | ||||||
|  |     libgcc.a ( * ) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .ARM.attributes 0       : { *(.ARM.attributes) } | ||||||
|  |    MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED | ||||||
|  |    MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM_SHARED | ||||||
|  |    MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										264
									
								
								bootloader/targets/f7/target.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								bootloader/targets/f7/target.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | |||||||
|  | #include <target.h> | ||||||
|  | #include <stm32wbxx.h> | ||||||
|  | #include <stm32wbxx_ll_system.h> | ||||||
|  | #include <stm32wbxx_ll_bus.h> | ||||||
|  | #include <stm32wbxx_ll_utils.h> | ||||||
|  | #include <stm32wbxx_ll_rcc.h> | ||||||
|  | #include <stm32wbxx_ll_rtc.h> | ||||||
|  | #include <stm32wbxx_ll_pwr.h> | ||||||
|  | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | #include <stm32wbxx_hal_flash.h> | ||||||
|  | 
 | ||||||
|  | #include <lib/toolbox/version.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <u8g2.h> | ||||||
|  | 
 | ||||||
|  | const uint8_t I_DFU_128x50[] = { | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0A, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x0F, 0x00, 0xC0, 0xE0, 0x4F, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x1E, 0x90, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, 0x00, 0x8C, 0x01, 0xA0, 0x00, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xFF, 0x19, 0x00, 0x63, 0x00, 0xC0, 0xF0, 0x07, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x5E, 0x1F, 0x80, 0x18, 0x00, 0xE0, 0x0E, 0x18, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0xAF, 0x0F, 0x40, 0x06, 0x00, 0xF8, 0x01, 0x20, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x57, 0x01, 0x20, 0x01, 0x00, 0x78, 0x00, 0x3E, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x81, 0xAF, 0x02, 0x90, 0x00, 0x00, 0x38, 0x80, 0x41, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x80, 0x57, 0x01, 0x48, 0x00, 0x00, 0x10, 0x60, 0x40, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x80, 0xAB, 0x00, 0x24, 0x00, 0x00, 0x08, 0x10, 0x40, | ||||||
|  |     0x3F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0xC0, 0x57, 0x01, 0x12, 0x00, 0x00, 0x04, 0x08, 0x40, | ||||||
|  |     0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x03, 0xF0, 0xAB, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x04, 0x40, | ||||||
|  |     0x00, 0xF0, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0xFC, 0x55, 0x01, 0x05, 0xE0, 0x00, 0x01, 0x04, 0x40, | ||||||
|  |     0x00, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0x00, 0xFF, 0xAB, 0x00, 0x05, 0xE0, 0x80, 0x00, 0x02, 0x40, | ||||||
|  |     0x0F, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xE0, 0xCF, 0x55, 0x81, 0x02, 0xF0, 0x40, 0x00, 0x02, 0x40, | ||||||
|  |     0xF0, 0x0F, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0xC3, 0xAB, 0x80, 0x02, 0x78, 0x20, 0x00, 0x01, 0x40, | ||||||
|  |     0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xC0, 0xD5, 0x81, 0x01, 0x7E, 0x10, 0x80, 0x00, 0x20, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x0F, 0xE0, 0xFA, 0x83, 0xC1, 0x3F, 0x08, 0x80, 0x00, 0x20, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0xD8, 0x07, 0x83, 0xF1, 0x1F, 0x04, 0x40, 0x00, 0x20, | ||||||
|  |     0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xC7, 0x01, 0x83, 0xF1, 0x0F, 0x00, 0x20, 0x00, 0x10, | ||||||
|  |     0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0x7F, 0x40, 0x80, 0x83, 0xE1, 0x01, 0x00, 0x20, 0x00, 0x18, | ||||||
|  |     0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x00, 0x20, 0xFC, 0x83, 0x01, 0x00, 0x00, 0x10, 0x00, 0x18, | ||||||
|  |     0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x10, 0xD7, 0x01, 0x03, 0x00, 0x00, 0x08, 0x00, 0x1C, | ||||||
|  |     0xFF, 0xFF, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x88, 0xAB, 0x02, 0xE3, 0x01, 0x00, 0x08, 0x00, 0x0C, | ||||||
|  |     0xFF, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC4, 0x55, 0x05, 0x1E, 0x00, 0x00, 0x04, 0x00, 0x0E, | ||||||
|  |     0x7F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA3, 0xAB, 0x02, 0x06, 0x00, 0x00, 0x02, 0x00, 0x0F, | ||||||
|  |     0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x10, 0x57, 0x05, 0x02, 0x00, 0x00, 0x01, 0x80, 0x07, | ||||||
|  |     0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x08, 0xAB, 0x0A, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x07, | ||||||
|  |     0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x84, 0x57, 0x15, 0x01, 0x00, 0x30, 0x00, 0xE0, 0x07, | ||||||
|  |     0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0x2A, 0x01, 0x00, 0x0C, 0x00, 0xF0, 0x0F, | ||||||
|  |     0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0xE0, 0xFE, 0x55, 0x01, 0x82, 0x03, 0x00, 0xF8, 0x15, | ||||||
|  |     0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x78, 0xFE, 0xAA, 0x01, 0x7C, 0x00, 0x00, 0xFC, 0x23, | ||||||
|  |     0x00, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0x0C, 0x3C, 0x7F, 0x5D, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x45, | ||||||
|  |     0xC0, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x8F, 0xBF, 0xAE, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0x82, | ||||||
|  |     0x30, 0x00, 0x00, 0xC0, 0x01, 0x80, 0xC1, 0x43, 0xFE, 0x5D, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x05, | ||||||
|  |     0x0F, 0x00, 0x80, 0x3F, 0x00, 0x60, 0xF0, 0x31, 0xF6, 0xAE, 0x03, 0x00, 0x00, 0xFA, 0xAF, 0x02, | ||||||
|  |     0xFC, 0xFF, 0x7F, 0x00, 0x00, 0x18, 0x7C, 0x08, 0x23, 0xFF, 0x05, 0x00, 0x00, 0xFD, 0x55, 0x01, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x1F, 0x84, 0x30, 0xFE, 0x0A, 0x00, 0x00, 0xAA, 0xAA, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0x80, 0xF1, 0x07, 0x43, 0x18, 0xFF, 0x15, 0x00, 0x00, 0x54, 0x15, 0x00, | ||||||
|  |     0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x80, 0x20, 0x8C, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Boot request enum
 | ||||||
|  | #define BOOT_REQUEST_TAINTED 0x00000000 | ||||||
|  | #define BOOT_REQUEST_CLEAN 0xDADEDADE | ||||||
|  | #define BOOT_REQUEST_DFU 0xDF00B000 | ||||||
|  | // Boot to DFU pin
 | ||||||
|  | #define BOOT_DFU_PORT GPIOB | ||||||
|  | #define BOOT_DFU_PIN LL_GPIO_PIN_11 | ||||||
|  | // USB pins
 | ||||||
|  | #define BOOT_USB_PORT GPIOA | ||||||
|  | #define BOOT_USB_DM_PIN LL_GPIO_PIN_11 | ||||||
|  | #define BOOT_USB_DP_PIN LL_GPIO_PIN_12 | ||||||
|  | #define BOOT_USB_PIN (BOOT_USB_DM_PIN | BOOT_USB_DP_PIN) | ||||||
|  | 
 | ||||||
|  | #define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) | ||||||
|  | 
 | ||||||
|  | uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); | ||||||
|  | uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); | ||||||
|  | 
 | ||||||
|  | void target_led_control(char* c) { | ||||||
|  |     furi_hal_light_set(LightRed, 0x00); | ||||||
|  |     furi_hal_light_set(LightGreen, 0x00); | ||||||
|  |     furi_hal_light_set(LightBlue, 0x00); | ||||||
|  |     do { | ||||||
|  |         if(*c == 'R') { | ||||||
|  |             furi_hal_light_set(LightRed, 0xFF); | ||||||
|  |         } else if(*c == 'G') { | ||||||
|  |             furi_hal_light_set(LightGreen, 0xFF); | ||||||
|  |         } else if(*c == 'B') { | ||||||
|  |             furi_hal_light_set(LightBlue, 0xFF); | ||||||
|  |         } else if(*c == '.') { | ||||||
|  |             LL_mDelay(125); | ||||||
|  |             furi_hal_light_set(LightRed, 0x00); | ||||||
|  |             furi_hal_light_set(LightGreen, 0x00); | ||||||
|  |             furi_hal_light_set(LightBlue, 0x00); | ||||||
|  |             LL_mDelay(125); | ||||||
|  |         } else if(*c == '-') { | ||||||
|  |             LL_mDelay(250); | ||||||
|  |             furi_hal_light_set(LightRed, 0x00); | ||||||
|  |             furi_hal_light_set(LightGreen, 0x00); | ||||||
|  |             furi_hal_light_set(LightBlue, 0x00); | ||||||
|  |             LL_mDelay(250); | ||||||
|  |         } else if(*c == '|') { | ||||||
|  |             furi_hal_light_set(LightRed, 0x00); | ||||||
|  |             furi_hal_light_set(LightGreen, 0x00); | ||||||
|  |             furi_hal_light_set(LightBlue, 0x00); | ||||||
|  |         } | ||||||
|  |         c++; | ||||||
|  |     } while(*c != 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_clock_init() { | ||||||
|  |     LL_Init1msTick(4000000); | ||||||
|  |     LL_SetSystemCoreClock(4000000); | ||||||
|  | 
 | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE); | ||||||
|  |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH); | ||||||
|  | 
 | ||||||
|  |     LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); | ||||||
|  |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_gpio_init() { | ||||||
|  |     // USB D+
 | ||||||
|  |     LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_MODE_OUTPUT); | ||||||
|  |     LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |     LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |     // USB D-
 | ||||||
|  |     LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_MODE_OUTPUT); | ||||||
|  |     LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |     LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |     // Button: back
 | ||||||
|  |     LL_GPIO_SetPinMode(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_MODE_INPUT); | ||||||
|  |     LL_GPIO_SetPinPull(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_PULL_UP); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_rtc_init() { | ||||||
|  |     // LSE and RTC
 | ||||||
|  |     LL_PWR_EnableBkUpAccess(); | ||||||
|  |     if(!RTC_CLOCK_IS_READY()) { | ||||||
|  |         // Start LSI1 needed for CSS
 | ||||||
|  |         LL_RCC_LSI1_Enable(); | ||||||
|  |         // Try to start LSE normal way
 | ||||||
|  |         LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); | ||||||
|  |         LL_RCC_LSE_Enable(); | ||||||
|  |         uint32_t c = 0; | ||||||
|  |         while(!RTC_CLOCK_IS_READY() && c < 200) { | ||||||
|  |             LL_mDelay(10); | ||||||
|  |             c++; | ||||||
|  |         } | ||||||
|  |         // Plan B: reset backup domain
 | ||||||
|  |         if(!RTC_CLOCK_IS_READY()) { | ||||||
|  |             target_led_control("-R.R.R."); | ||||||
|  |             LL_RCC_ForceBackupDomainReset(); | ||||||
|  |             LL_RCC_ReleaseBackupDomainReset(); | ||||||
|  |             NVIC_SystemReset(); | ||||||
|  |         } | ||||||
|  |         // Set RTC domain clock to LSE
 | ||||||
|  |         LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); | ||||||
|  |         // Enable LSE CSS
 | ||||||
|  |         LL_RCC_LSE_EnableCSS(); | ||||||
|  |     } | ||||||
|  |     // Enable clocking
 | ||||||
|  |     LL_RCC_EnableRTC(); | ||||||
|  |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_version_save(void) { | ||||||
|  |     LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR1, (uint32_t)version_get()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_usb_wire_reset() { | ||||||
|  |     LL_GPIO_ResetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_display_init() { | ||||||
|  |     // Prepare gpio
 | ||||||
|  |     hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull); | ||||||
|  |     hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); | ||||||
|  |     // Initialize
 | ||||||
|  |     u8g2_t fb; | ||||||
|  |     u8g2_Setup_st7565_erc12864_alt_f(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); | ||||||
|  |     u8g2_InitDisplay(&fb); | ||||||
|  |     u8g2_SetContrast(&fb, 36); | ||||||
|  |     // Create payload
 | ||||||
|  |     u8g2_ClearBuffer(&fb); | ||||||
|  |     u8g2_SetDrawColor(&fb, 0x01); | ||||||
|  |     u8g2_SetFont(&fb, u8g2_font_helvB08_tf); | ||||||
|  |     u8g2_DrawXBM(&fb, 0, 64 - 50, 128, 50, I_DFU_128x50); | ||||||
|  |     u8g2_DrawStr(&fb, 2, 8, "Update & Recovery Mode"); | ||||||
|  |     u8g2_DrawStr(&fb, 2, 21, "DFU started"); | ||||||
|  |     // Send buffer
 | ||||||
|  |     u8g2_SetPowerSave(&fb, 0); | ||||||
|  |     u8g2_SendBuffer(&fb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_init() { | ||||||
|  |     target_clock_init(); | ||||||
|  |     target_gpio_init(); | ||||||
|  |     furi_hal_init(); | ||||||
|  |     target_led_control("RGB"); | ||||||
|  |     target_rtc_init(); | ||||||
|  |     target_version_save(); | ||||||
|  |     target_usb_wire_reset(); | ||||||
|  | 
 | ||||||
|  |     // Errata 2.2.9, Flash OPTVERR flag is always set after system reset
 | ||||||
|  |     __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int target_is_dfu_requested() { | ||||||
|  |     if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_TAINTED) { | ||||||
|  |         // Default system state is tainted
 | ||||||
|  |         // We must ensure that MCU is cleanly booted
 | ||||||
|  |         LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN); | ||||||
|  |         NVIC_SystemReset(); | ||||||
|  |     } else if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_DFU) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     LL_mDelay(100); | ||||||
|  |     if(!LL_GPIO_IsInputPinSet(BOOT_DFU_PORT, BOOT_DFU_PIN)) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_switch(void* offset) { | ||||||
|  |     asm volatile("ldr    r3, [%0]    \n" | ||||||
|  |                  "msr    msp, r3     \n" | ||||||
|  |                  "ldr    r3, [%1]    \n" | ||||||
|  |                  "mov    pc, r3      \n" | ||||||
|  |                  : | ||||||
|  |                  : "r"(offset), "r"(offset + 0x4) | ||||||
|  |                  : "r3"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_switch2dfu() { | ||||||
|  |     target_led_control("B"); | ||||||
|  |     furi_hal_light_set(LightBacklight, 0xFF); | ||||||
|  |     target_display_init(); | ||||||
|  |     // Mark system as tainted, it will be soon
 | ||||||
|  |     LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED); | ||||||
|  |     // Remap memory to system bootloader
 | ||||||
|  |     LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH); | ||||||
|  |     // Jump
 | ||||||
|  |     target_switch(0x0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void target_switch2os() { | ||||||
|  |     target_led_control("G"); | ||||||
|  |     SCB->VTOR = OS_OFFSET; | ||||||
|  |     target_switch((void*)(BOOT_ADDRESS + OS_OFFSET)); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								bootloader/targets/f7/target.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								bootloader/targets/f7/target.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | TOOLCHAIN = arm | ||||||
|  | 
 | ||||||
|  | BOOT_ADDRESS	= 0x08000000 | ||||||
|  | FW_ADDRESS		= 0x08008000 | ||||||
|  | OS_OFFSET		= 0x00008000 | ||||||
|  | FLASH_ADDRESS	= 0x08000000 | ||||||
|  | 
 | ||||||
|  | OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" | ||||||
|  | BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) | ||||||
|  | MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | ||||||
|  | 
 | ||||||
|  | CFLAGS			+= $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections | ||||||
|  | LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs  | ||||||
|  | 
 | ||||||
|  | CUBE_DIR		= $(PROJECT_ROOT)/lib/STM32CubeWB | ||||||
|  | 
 | ||||||
|  | # ST HAL
 | ||||||
|  | CFLAGS			+=  -DUSE_FULL_LL_DRIVER | ||||||
|  | ASM_SOURCES		+= $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.s | ||||||
|  | C_SOURCES		+= $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/system_stm32wbxx.c | ||||||
|  | C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c | ||||||
|  | C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_gpio.c | ||||||
|  | C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_i2c.c | ||||||
|  | C_SOURCES		+= $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c | ||||||
|  | 
 | ||||||
|  | CFLAGS			+= -I$(CUBE_DIR)/Drivers/CMSIS/Include | ||||||
|  | CFLAGS			+= -I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Include | ||||||
|  | CFLAGS			+= -I$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Inc | ||||||
|  | 
 | ||||||
|  | LDFLAGS			+= -T$(TARGET_DIR)/stm32wb55xx_flash_cm4.ld | ||||||
|  | 
 | ||||||
|  | # Drivers
 | ||||||
|  | DRIVERS_DIR		= $(PROJECT_ROOT)//lib/drivers | ||||||
|  | CFLAGS			+= -I$(DRIVERS_DIR) | ||||||
|  | C_SOURCES		+= $(DRIVERS_DIR)/lp5562.c | ||||||
|  | 
 | ||||||
|  | # API-HAL
 | ||||||
|  | CFLAGS			+= -I$(TARGET_DIR)/furi-hal | ||||||
|  | C_SOURCES		+= $(wildcard $(TARGET_DIR)/furi-hal/*.c) | ||||||
|  | 
 | ||||||
|  | # Version generation
 | ||||||
|  | C_SOURCES		+= $(PROJECT_ROOT)/lib/toolbox/version.c | ||||||
|  | 
 | ||||||
|  | ASM_SOURCES		+= $(wildcard $(TARGET_DIR)/*.s) | ||||||
|  | C_SOURCES		+= $(wildcard $(TARGET_DIR)/*.c) | ||||||
|  | CPP_SOURCES		+= $(wildcard $(TARGET_DIR)/*.cpp) | ||||||
|  | 
 | ||||||
|  | SVD_FILE		= $(PROJECT_ROOT)/debug/STM32WB55_CM4.svd | ||||||
| @ -19,6 +19,15 @@ | |||||||
|     }) |     }) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef ROUND_UP_TO | ||||||
|  | #define ROUND_UP_TO(a, b)       \ | ||||||
|  |     ({                          \ | ||||||
|  |         __typeof__(a) _a = (a); \ | ||||||
|  |         __typeof__(b) _b = (b); \ | ||||||
|  |         _a / _b + !!(_a % _b);  \ | ||||||
|  |     }) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifndef CLAMP | #ifndef CLAMP | ||||||
| #define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) | #define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) | ||||||
| #endif | #endif | ||||||
| @ -40,3 +49,11 @@ | |||||||
|         y = SWAP;           \ |         y = SWAP;           \ | ||||||
|     } while(0) |     } while(0) | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef PLACE_IN_SECTION | ||||||
|  | #define PLACE_IN_SECTION(x) __attribute__((section(x))) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef ALIGN | ||||||
|  | #define ALIGN(n) __attribute__((aligned(n))) | ||||||
|  | #endif | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ CLANG_FORMAT_BIN="/usr/bin/clang-format-12" | |||||||
| 
 | 
 | ||||||
| PROJECT_DIR=$(pwd) | PROJECT_DIR=$(pwd) | ||||||
| 
 | 
 | ||||||
| cd $PROJECT_DIR | cd "$PROJECT_DIR" || exit  | ||||||
| 
 | 
 | ||||||
| echo "RUN C\C++ SYNTAX CHECK" | echo "RUN C\C++ SYNTAX CHECK" | ||||||
| C_FILES=$(find . \ | C_FILES=$(find . \ | ||||||
| @ -34,7 +34,7 @@ fi | |||||||
| 
 | 
 | ||||||
| read -p "Do you want fix syntax? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1 | read -p "Do you want fix syntax? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1 | ||||||
| 
 | 
 | ||||||
| cd $PROJECT_DIR | cd "$PROJECT_DIR" || exit  | ||||||
| 
 | 
 | ||||||
| # We use root in container and clang-format rewriting files. We'll need change owner to original | # We use root in container and clang-format rewriting files. We'll need change owner to original | ||||||
| local_user=$(stat -c '%u' .clang-format) | local_user=$(stat -c '%u' .clang-format) | ||||||
|  | |||||||
| @ -118,6 +118,14 @@ to exclude the API function. */ | |||||||
| #define INCLUDE_xTaskGetSchedulerState       1 | #define INCLUDE_xTaskGetSchedulerState       1 | ||||||
| #define INCLUDE_xTimerPendFunctionCall       1 | #define INCLUDE_xTimerPendFunctionCall       1 | ||||||
| 
 | 
 | ||||||
|  | /* CMSIS-RTOS V2 flags */ | ||||||
|  | #define configUSE_OS2_THREAD_SUSPEND_RESUME  1 | ||||||
|  | #define configUSE_OS2_THREAD_ENUMERATE       1 | ||||||
|  | #define configUSE_OS2_EVENTFLAGS_FROM_ISR    1 | ||||||
|  | #define configUSE_OS2_THREAD_FLAGS           1 | ||||||
|  | #define configUSE_OS2_TIMER                  1 | ||||||
|  | #define configUSE_OS2_MUTEX                  1 | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used |  * The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used | ||||||
|  * by the application thus the correct define need to be enabled below |  * by the application thus the correct define need to be enabled below | ||||||
|  | |||||||
| @ -6,105 +6,20 @@ | |||||||
| #include "ble.h" | #include "ble.h" | ||||||
| #include "tl.h" | #include "tl.h" | ||||||
| #include "app_ble.h" | #include "app_ble.h" | ||||||
| 
 |  | ||||||
| #include "cmsis_os.h" |  | ||||||
| #include "shci.h" | #include "shci.h" | ||||||
| #include "otp.h" | #include "cmsis_os.h" | ||||||
| #include "dis_app.h" |  | ||||||
| #include "hrs_app.h" |  | ||||||
| 
 | 
 | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| 
 | 
 | ||||||
| typedef struct _tSecurityParams { |  | ||||||
|   uint8_t ioCapability; |  | ||||||
|   uint8_t mitm_mode; |  | ||||||
|   uint8_t bonding_mode; |  | ||||||
|   uint8_t Use_Fixed_Pin; |  | ||||||
|   uint8_t encryptionKeySizeMin; |  | ||||||
|   uint8_t encryptionKeySizeMax; |  | ||||||
|   uint32_t Fixed_Pin; |  | ||||||
|   uint8_t initiateSecurity; |  | ||||||
| } tSecurityParams; |  | ||||||
| 
 |  | ||||||
| typedef struct _tBLEProfileGlobalContext { |  | ||||||
|   tSecurityParams bleSecurityParam; |  | ||||||
|   uint16_t gapServiceHandle; |  | ||||||
|   uint16_t devNameCharHandle; |  | ||||||
|   uint16_t appearanceCharHandle; |  | ||||||
|   uint16_t connectionHandle; |  | ||||||
|   uint8_t advtServUUIDlen; |  | ||||||
|   uint8_t advtServUUID[100]; |  | ||||||
| } BleGlobalContext_t; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|   BleGlobalContext_t BleApplicationContext_legacy; |  | ||||||
|   APP_BLE_ConnStatus_t Device_Connection_Status; |  | ||||||
|   uint8_t Advertising_mgr_timer_Id; |  | ||||||
| } BleApplicationContext_t; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #define FAST_ADV_TIMEOUT               (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */ |  | ||||||
| #define INITIAL_ADV_TIMEOUT            (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */ |  | ||||||
| 
 |  | ||||||
| #define BD_ADDR_SIZE_LOCAL    6 |  | ||||||
| 
 |  | ||||||
| #define LED_ON_TIMEOUT                 (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */ |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | ||||||
| 
 | 
 | ||||||
| static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = | // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
 | ||||||
|     { | // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
 | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40) |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
| static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL]; |  | ||||||
| 
 |  | ||||||
| static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK; |  | ||||||
| static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ; |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext; |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax; |  | ||||||
| 
 |  | ||||||
| uint8_t  manuf_data[14] = { |  | ||||||
|     sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA, |  | ||||||
|     0x01/*SKD version */, |  | ||||||
|     0x00 /* Generic*/, |  | ||||||
|     0x00 /* GROUP A Feature  */, |  | ||||||
|     0x00 /* GROUP A Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00, /* BLE MAC start -MSB */ |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, /* BLE MAC stop */ |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| osMutexId_t MtxHciId; | osMutexId_t MtxHciId; | ||||||
| osSemaphoreId_t SemHciId; | osSemaphoreId_t SemHciId; | ||||||
| osThreadId_t AdvUpdateProcessId; |  | ||||||
| osThreadId_t HciUserEvtProcessId; | osThreadId_t HciUserEvtProcessId; | ||||||
| 
 | 
 | ||||||
| const osThreadAttr_t AdvUpdateProcess_attr = { |  | ||||||
|     .name = CFG_ADV_UPDATE_PROCESS_NAME, |  | ||||||
|     .attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS, |  | ||||||
|     .cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM, |  | ||||||
|     .cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE, |  | ||||||
|     .stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM, |  | ||||||
|     .priority = CFG_ADV_UPDATE_PROCESS_PRIORITY, |  | ||||||
|     .stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const osThreadAttr_t HciUserEvtProcess_attr = { | const osThreadAttr_t HciUserEvtProcess_attr = { | ||||||
|     .name = CFG_HCI_USER_EVT_PROCESS_NAME, |     .name = CFG_HCI_USER_EVT_PROCESS_NAME, | ||||||
|     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, |     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, | ||||||
| @ -120,14 +35,6 @@ static void HciUserEvtProcess(void *argument); | |||||||
| static void BLE_UserEvtRx( void * pPayload ); | static void BLE_UserEvtRx( void * pPayload ); | ||||||
| static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | ||||||
| static void Ble_Tl_Init( void ); | static void Ble_Tl_Init( void ); | ||||||
| static void Ble_Hci_Gap_Gatt_Init(); |  | ||||||
| static const uint8_t* BleGetBdAddress( void ); |  | ||||||
| static void Adv_Request( APP_BLE_ConnStatus_t New_Status ); |  | ||||||
| static void Add_Advertisment_Service_UUID( uint16_t servUUID ); |  | ||||||
| static void Adv_Mgr( void ); |  | ||||||
| static void AdvUpdateProcess(void *argument); |  | ||||||
| static void Adv_Update( void ); |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| bool APP_BLE_Init() { | bool APP_BLE_Init() { | ||||||
|   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||||
| @ -160,278 +67,6 @@ bool APP_BLE_Init() { | |||||||
|   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); |   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool APP_BLE_Start() { |  | ||||||
|   if (APPE_Status() != BleGlueStatusStarted) { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   // Initialization of HCI & GATT & GAP layer
 |  | ||||||
|   Ble_Hci_Gap_Gatt_Init(); |  | ||||||
|   // Initialization of the BLE Services
 |  | ||||||
|   SVCCTL_Init(); |  | ||||||
|   // Initialization of the BLE App Context
 |  | ||||||
|   BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF; |  | ||||||
|   // From here, all initialization are BLE application specific
 |  | ||||||
|   AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr); |  | ||||||
| 
 |  | ||||||
|   // Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
 |  | ||||||
| #if(BLE_CFG_OTA_REBOOT_CHAR != 0) |  | ||||||
|   manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   // Initialize DIS Application
 |  | ||||||
|   DISAPP_Init(); |  | ||||||
|   // Initialize HRS Application
 |  | ||||||
|   HRSAPP_Init(); |  | ||||||
|   // Create timer to handle the connection state machine
 |  | ||||||
|   HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr); |  | ||||||
| 
 |  | ||||||
|   // Make device discoverable
 |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1; |  | ||||||
|   Add_Advertisment_Service_UUID(HEART_RATE_SERVICE_UUID); |  | ||||||
|   /* Initialize intervals for reconnexion without intervals update */ |  | ||||||
|   AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN; |  | ||||||
|   AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX; |  | ||||||
| 
 |  | ||||||
|   Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) |  | ||||||
| { |  | ||||||
|   hci_event_pckt *event_pckt; |  | ||||||
|   evt_le_meta_event *meta_evt; |  | ||||||
|   evt_blue_aci *blue_evt; |  | ||||||
|   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; |  | ||||||
|   uint8_t TX_PHY, RX_PHY; |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
| 
 |  | ||||||
|   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; |  | ||||||
| 
 |  | ||||||
|   switch (event_pckt->evt) { |  | ||||||
|     case EVT_DISCONN_COMPLETE: |  | ||||||
|     { |  | ||||||
|       hci_disconnection_complete_event_rp0 *disconnection_complete_event; |  | ||||||
|       disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; |  | ||||||
| 
 |  | ||||||
|       if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0; |  | ||||||
|         BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|         APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n"); |  | ||||||
|       } |  | ||||||
|       /* restart advertising */ |  | ||||||
|       Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|       furi_hal_power_insomnia_exit(); |  | ||||||
|     } |  | ||||||
|     break; /* EVT_DISCONN_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|     case EVT_LE_META_EVENT: |  | ||||||
|     { |  | ||||||
|       meta_evt = (evt_le_meta_event*) event_pckt->data; |  | ||||||
|       switch (meta_evt->subevent) |  | ||||||
|       { |  | ||||||
|         case EVT_LE_CONN_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n"); |  | ||||||
| 
 |  | ||||||
|           /* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|           /* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_PHY_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n"); |  | ||||||
|           evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; |  | ||||||
|           if (evt_le_phy_update_complete->Status == 0) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n"); |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n"); |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY); |  | ||||||
|           if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read_PHY success \r\n"); |  | ||||||
| 
 |  | ||||||
|             if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read conf not succeess \r\n"); |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_CONN_COMPLETE: |  | ||||||
|         { |  | ||||||
|           furi_hal_power_insomnia_enter(); |  | ||||||
|           hci_le_connection_complete_event_rp0 *connection_complete_event; |  | ||||||
| 
 |  | ||||||
|           /**
 |  | ||||||
|            * The connection is done, there is no need anymore to schedule the LP ADV |  | ||||||
|            */ |  | ||||||
|           connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; |  | ||||||
| 
 |  | ||||||
|           HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle); |  | ||||||
|           if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING) |  | ||||||
|           { |  | ||||||
|             /* Connection as client */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT; |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             /* Connection as server */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER; |  | ||||||
|           } |  | ||||||
|           BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle; |  | ||||||
|         } |  | ||||||
|         break; /* HCI_EVT_LE_CONN_COMPLETE */ |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     break; /* HCI_EVT_LE_META_EVENT */ |  | ||||||
| 
 |  | ||||||
|     case EVT_VENDOR: |  | ||||||
|       blue_evt = (evt_blue_aci*) event_pckt->data; |  | ||||||
|       switch (blue_evt->ecode) { |  | ||||||
|         aci_gap_pairing_complete_event_rp0 *pairing_complete; |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:  |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */ |  | ||||||
|            |  | ||||||
|       case EVT_BLUE_GAP_PASS_KEY_REQUEST:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n"); |  | ||||||
| 
 |  | ||||||
|         aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456); |  | ||||||
| 
 |  | ||||||
|         APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:    |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_BOND_LOST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n"); |  | ||||||
|           aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); |  | ||||||
|         APP_DBG_MSG("\r\n\r** Send allow rebond \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_BOND_LOST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_DEVICE_FOUND:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
|        |  | ||||||
|       case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION): |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */     |  | ||||||
| 
 |  | ||||||
|        case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE): |  | ||||||
|           APP_DBG_MSG("numeric_value = %ld\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("Hex_value = %lx\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */ |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n"); |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (EVT_BLUE_GAP_PAIRING_CMPLT): |  | ||||||
|           { |  | ||||||
|             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; |  | ||||||
| 
 |  | ||||||
|             APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status); |  | ||||||
|             if (pairing_complete->Status == 0) { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing OK \r\n"); |  | ||||||
|             } else { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing KO \r\n"); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|       /* USER CODE END ecode */ |  | ||||||
|         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n"); |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|       break; /* EVT_VENDOR */ |  | ||||||
|       default: |  | ||||||
|         break; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return (SVCCTL_UserEvtFlowEnable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() { |  | ||||||
|     return BleApplicationContext.Device_Connection_Status; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* USER CODE BEGIN FD*/ |  | ||||||
| void APP_BLE_Key_Button1_Action() { |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   ret = aci_gap_clear_security_db(); |  | ||||||
|   if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|     APP_DBG_MSG("Successfully aci_gap_clear_security_db()\r\n"); |  | ||||||
|   } else { |  | ||||||
|     APP_DBG_MSG("aci_gap_clear_security_db() Failed , result: %d \r\n", ret); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void APP_BLE_Key_Button2_Action() { |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   ret = aci_gap_slave_security_req(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);  |  | ||||||
|   if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|     APP_DBG_MSG("Successfully aci_gap_slave_security_req()"); |  | ||||||
|   } else { |  | ||||||
|     APP_DBG_MSG("aci_gap_slave_security_req() Failed , result: %d \r\n", ret); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|    |  | ||||||
| void APP_BLE_Key_Button3_Action() { |  | ||||||
|   uint8_t TX_PHY, RX_PHY; |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY); |  | ||||||
|   if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|     APP_DBG_MSG("Read_PHY success \r\n"); |  | ||||||
|     APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|     if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) { |  | ||||||
|       APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \r\n", TX_1M, RX_1M); |  | ||||||
|       ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_1M,RX_1M,0); |  | ||||||
|     } else { |  | ||||||
|       APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \r\n", TX_2M_PREFERRED, RX_2M_PREFERRED); |  | ||||||
|       ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED,0); |  | ||||||
|     }  |  | ||||||
|   } else { |  | ||||||
|     APP_DBG_MSG("Read conf not succeess \r\n"); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|     APP_DBG_MSG("set PHY cmd ok\r\n"); |  | ||||||
|   } else { |  | ||||||
|     APP_DBG_MSG("set PHY cmd NOK\r\n"); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Ble_Tl_Init( void ) { | static void Ble_Tl_Init( void ) { | ||||||
|   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; |   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; | ||||||
| 
 | 
 | ||||||
| @ -443,308 +78,6 @@ static void Ble_Tl_Init( void ) { | |||||||
|   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); |   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void Ble_Hci_Gap_Gatt_Init() { |  | ||||||
|   uint8_t role; |  | ||||||
|   uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t srd_bd_addr[2]; |  | ||||||
|   uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE }; |  | ||||||
| 
 |  | ||||||
|   /*HCI Reset to synchronise BLE Stack*/ |  | ||||||
|   hci_reset(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write the BD Address |  | ||||||
|    */ |  | ||||||
|   bd_addr = BleGetBdAddress(); |  | ||||||
|   aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, |  | ||||||
|                             CONFIG_DATA_PUBADDR_LEN, |  | ||||||
|                             (uint8_t*) bd_addr); |  | ||||||
| 
 |  | ||||||
|   /* BLE MAC in ADV Packet */ |  | ||||||
|   manuf_data[ sizeof(manuf_data)-6] = bd_addr[5]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-5] = bd_addr[4]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-4] = bd_addr[3]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-3] = bd_addr[2]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-2] = bd_addr[1]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-1] = bd_addr[0]; |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, |  | ||||||
|     CONFIG_DATA_IR_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_IR_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET, |  | ||||||
|     CONFIG_DATA_ER_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_ER_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write random bd_address |  | ||||||
|    */ |  | ||||||
|    /* random_bd_address = R_bd_address;
 |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR, |  | ||||||
|     CONFIG_DATA_RANDOM_ADDRESS_LEN, |  | ||||||
|                             (uint8_t*) random_bd_address); |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Static random Address |  | ||||||
|    * The two upper bits shall be set to 1 |  | ||||||
|    * The lowest 32bits is read from the UDN to differentiate between devices |  | ||||||
|    * The RNG may be used to provide a random number on each power on |  | ||||||
|    */ |  | ||||||
|   srd_bd_addr[1] =  0x0000ED6E; |  | ||||||
|   srd_bd_addr[0] =  LL_FLASH_GetUDN( ); |  | ||||||
|   aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE ); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Set TX Power to 0dBm. |  | ||||||
|    */ |  | ||||||
|   aci_hal_set_tx_power_level(1, CFG_TX_POWER); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GATT interface |  | ||||||
|    */ |  | ||||||
|   aci_gatt_init(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GAP interface |  | ||||||
|    */ |  | ||||||
|   role = 0; |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_PERIPHERAL == 1) |  | ||||||
|   role |= GAP_PERIPHERAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_CENTRAL == 1) |  | ||||||
|   role |= GAP_CENTRAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   if (role > 0) |  | ||||||
|   { |  | ||||||
|     const char *name = furi_hal_version_get_device_name_ptr(); |  | ||||||
|     aci_gap_init(role, 0, |  | ||||||
|                  strlen(name), |  | ||||||
|                  &gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle); |  | ||||||
| 
 |  | ||||||
|     if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name)) |  | ||||||
|     { |  | ||||||
|       BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(aci_gatt_update_char_value(gap_service_handle, |  | ||||||
|                                 gap_appearance_char_handle, |  | ||||||
|                                 0, |  | ||||||
|                                 2, |  | ||||||
|                                 (uint8_t *)&appearance)) |  | ||||||
|   { |  | ||||||
|     BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|   } |  | ||||||
|   /**
 |  | ||||||
|    * Initialize Default PHY |  | ||||||
|    */ |  | ||||||
|   hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize IO capability |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY; |  | ||||||
|   aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize authentication |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE; |  | ||||||
| 
 |  | ||||||
|   aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode, |  | ||||||
|                                          CFG_SC_SUPPORT, |  | ||||||
|                                          CFG_KEYPRESS_NOTIFICATION_SUPPORT, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin, |  | ||||||
|                                          PUBLIC_ADDR |  | ||||||
|                                          ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize whitelist |  | ||||||
|    */ |  | ||||||
|    if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode) |  | ||||||
|    { |  | ||||||
|      aci_gap_configure_whitelist(); |  | ||||||
|    } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Request(APP_BLE_ConnStatus_t New_Status) |  | ||||||
| { |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   uint16_t Min_Inter, Max_Inter; |  | ||||||
| 
 |  | ||||||
|   if (New_Status == APP_BLE_FAST_ADV) |  | ||||||
|   { |  | ||||||
|     Min_Inter = AdvIntervalMin; |  | ||||||
|     Max_Inter = AdvIntervalMax; |  | ||||||
|   } |  | ||||||
|   else |  | ||||||
|   { |  | ||||||
|     Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN; |  | ||||||
|     Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Stop the timer, it will be restarted for a new shot |  | ||||||
|      * It does not hurt if the timer was not running |  | ||||||
|      */ |  | ||||||
|     HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|     APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status); |  | ||||||
| 
 |  | ||||||
|     if ((New_Status == APP_BLE_LP_ADV) |  | ||||||
|         && ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV) |  | ||||||
|             || (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV))) |  | ||||||
|     { |  | ||||||
|       /* Connection in ADVERTISE mode have to stop the current advertising */ |  | ||||||
|       ret = aci_gap_set_non_discoverable(); |  | ||||||
|       if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Successfully Stopped Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|       else |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     BleApplicationContext.Device_Connection_Status = New_Status; |  | ||||||
| 
 |  | ||||||
|     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); |  | ||||||
| 
 |  | ||||||
|     /* Start Fast or Low Power Advertising */ |  | ||||||
|     ret = aci_gap_set_discoverable( |  | ||||||
|         ADV_IND, |  | ||||||
|         Min_Inter, |  | ||||||
|         Max_Inter, |  | ||||||
|         PUBLIC_ADDR, |  | ||||||
|         NO_WHITE_LIST_USE, /* use white list */ |  | ||||||
|         strlen(name), |  | ||||||
|         (uint8_t*)name, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID, |  | ||||||
|         0, |  | ||||||
|         0); |  | ||||||
| 
 |  | ||||||
|     /* Update Advertising data */ |  | ||||||
|     ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data); |  | ||||||
|     if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Successfully Start Fast Advertising \r\n" ); |  | ||||||
|         /* Start Timer to STOP ADV - TIMEOUT */ |  | ||||||
|         HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Successfully Start Low Power Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|     } else { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const uint8_t* BleGetBdAddress( void ) { |  | ||||||
|   uint8_t *otp_addr; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t udn; |  | ||||||
|   uint32_t company_id; |  | ||||||
|   uint32_t device_id; |  | ||||||
| 
 |  | ||||||
|   udn = LL_FLASH_GetUDN(); |  | ||||||
| 
 |  | ||||||
|   if(udn != 0xFFFFFFFF) { |  | ||||||
|     company_id = LL_FLASH_GetSTCompanyID(); |  | ||||||
|     device_id = LL_FLASH_GetDeviceID(); |  | ||||||
| 
 |  | ||||||
|     bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF); |  | ||||||
|     bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); |  | ||||||
|     bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); |  | ||||||
|     bd_addr_udn[3] = (uint8_t)device_id; |  | ||||||
|     bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);; |  | ||||||
|     bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); |  | ||||||
| 
 |  | ||||||
|     bd_addr = (const uint8_t *)bd_addr_udn; |  | ||||||
|   } else { |  | ||||||
|     otp_addr = OTP_Read(0); |  | ||||||
|     if(otp_addr) { |  | ||||||
|       bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address; |  | ||||||
|     } else { |  | ||||||
|       bd_addr = M_bd_addr; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return bd_addr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  *SPECIFIC FUNCTIONS |  | ||||||
|  * |  | ||||||
|  *************************************************************/ |  | ||||||
| static void Add_Advertisment_Service_UUID( uint16_t servUUID ) { |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] = |  | ||||||
|       (uint8_t) (servUUID & 0xFF); |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] = |  | ||||||
|       (uint8_t) (servUUID >> 8) & 0xFF; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Mgr( void ) { |  | ||||||
|   /**
 |  | ||||||
|    * The code shall be executed in the background as an aci command may be sent |  | ||||||
|    * The background is the only place where the application can make sure a new aci command |  | ||||||
|    * is not sent if there is a pending one |  | ||||||
|    */ |  | ||||||
|   osThreadFlagsSet( AdvUpdateProcessId, 1 ); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void AdvUpdateProcess(void *argument) { |  | ||||||
|   UNUSED(argument); |  | ||||||
| 
 |  | ||||||
|   for(;;) { |  | ||||||
|     osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever); |  | ||||||
|     Adv_Update( ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Update( void ) { |  | ||||||
|   Adv_Request(APP_BLE_LP_ADV); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HciUserEvtProcess(void *argument) { | static void HciUserEvtProcess(void *argument) { | ||||||
|   UNUSED(argument); |   UNUSED(argument); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,24 +7,7 @@ extern "C" { | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include "hci_tl.h" | #include "hci_tl.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { |  | ||||||
|     APP_BLE_IDLE, |  | ||||||
|     APP_BLE_FAST_ADV, |  | ||||||
|     APP_BLE_LP_ADV, |  | ||||||
|     APP_BLE_SCAN, |  | ||||||
|     APP_BLE_LP_CONNECTING, |  | ||||||
|     APP_BLE_CONNECTED_SERVER, |  | ||||||
|     APP_BLE_CONNECTED_CLIENT |  | ||||||
| } APP_BLE_ConnStatus_t; |  | ||||||
| 
 |  | ||||||
| bool APP_BLE_Init(); | bool APP_BLE_Init(); | ||||||
| bool APP_BLE_Start(); |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(); |  | ||||||
| 
 |  | ||||||
| void APP_BLE_Key_Button1_Action(); |  | ||||||
| void APP_BLE_Key_Button2_Action(); |  | ||||||
| void APP_BLE_Key_Button3_Action(); |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,81 +32,12 @@ extern "C"{ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| 
 | 
 | ||||||
|  | #include <furi/common_defines.h> | ||||||
|  | 
 | ||||||
| #include "app_conf.h" | #include "app_conf.h" | ||||||
| 
 | 
 | ||||||
|   /* -------------------------------- *
 |  | ||||||
|    *  Basic definitions               * |  | ||||||
|    * -------------------------------- */ |  | ||||||
| 
 |  | ||||||
| #undef NULL |  | ||||||
| #define NULL                    0 |  | ||||||
| 
 |  | ||||||
| #undef FALSE |  | ||||||
| #define FALSE                   0 |  | ||||||
| 
 |  | ||||||
| #undef TRUE |  | ||||||
| #define TRUE                    (!0) |  | ||||||
| 
 |  | ||||||
|   /* -------------------------------- *
 |  | ||||||
|    *  Critical Section definition     * |  | ||||||
|    * -------------------------------- */ |  | ||||||
| #define BACKUP_PRIMASK()    uint32_t primask_bit= __get_PRIMASK() |  | ||||||
| #define DISABLE_IRQ()       __disable_irq() |  | ||||||
| #define RESTORE_PRIMASK()   __set_PRIMASK(primask_bit) |  | ||||||
| 
 |  | ||||||
|   /* -------------------------------- *
 |  | ||||||
|    *  Macro delimiters                * |  | ||||||
|    * -------------------------------- */ |  | ||||||
| 
 |  | ||||||
| #define M_BEGIN     do { |  | ||||||
| 
 |  | ||||||
| #define M_END       } while(0) |  | ||||||
| 
 |  | ||||||
|   /* -------------------------------- *
 |  | ||||||
|    *  Some useful macro definitions   * |  | ||||||
|    * -------------------------------- */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #define MODINC( a, m )       M_BEGIN  (a)++;  if ((a)>=(m)) (a)=0;  M_END |  | ||||||
| 
 |  | ||||||
| #define MODDEC( a, m )       M_BEGIN  if ((a)==0) (a)=(m);  (a)--;  M_END |  | ||||||
| 
 |  | ||||||
| #define MODADD( a, b, m )    M_BEGIN  (a)+=(b);  if ((a)>=(m)) (a)-=(m);  M_END |  | ||||||
| 
 |  | ||||||
| #define MODSUB( a, b, m )    MODADD( a, (m)-(b), m ) |  | ||||||
| 
 |  | ||||||
| #define PAUSE( t )           M_BEGIN \ |  | ||||||
|                                __IO int _i; \ |  | ||||||
|                                for ( _i = t; _i > 0; _i -- ); \ |  | ||||||
|                              M_END |  | ||||||
| 
 |  | ||||||
| #define DIVF( x, y )         ((x)/(y)) |  | ||||||
| 
 |  | ||||||
| #define DIVC( x, y )         (((x)+(y)-1)/(y)) | #define DIVC( x, y )         (((x)+(y)-1)/(y)) | ||||||
| 
 | 
 | ||||||
| #define DIVR( x, y )         (((x)+((y)/2))/(y)) | #define DIVR( x, y )         (((x)+((y)/2))/(y)) | ||||||
| 
 | 
 | ||||||
| #define SHRR( x, n )         ((((x)>>((n)-1))+1)>>1) |  | ||||||
| 
 |  | ||||||
| #define BITN( w, n )         (((w)[(n)/32] >> ((n)%32)) & 1) |  | ||||||
| 
 |  | ||||||
| #define BITNSET( w, n, b )   M_BEGIN (w)[(n)/32] |= ((U32)(b))<<((n)%32); M_END |  | ||||||
| 
 |  | ||||||
|   /* -------------------------------- *
 |  | ||||||
|    *  Compiler                         * |  | ||||||
|    * -------------------------------- */ |  | ||||||
| #define PLACE_IN_SECTION( __x__ )  __attribute__((section (__x__))) |  | ||||||
| 
 |  | ||||||
| #ifdef WIN32 |  | ||||||
| #define ALIGN(n) |  | ||||||
| #else |  | ||||||
| #define ALIGN(n)             __attribute__((aligned(n))) |  | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } /* extern "C" */ |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #endif /*APP_COMMON_H */ |  | ||||||
| 
 |  | ||||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |  | ||||||
|  | |||||||
| @ -139,7 +139,7 @@ | |||||||
| /**
 | /**
 | ||||||
|  * Maximum supported ATT_MTU size |  * Maximum supported ATT_MTU size | ||||||
|  */ |  */ | ||||||
| #define CFG_BLE_MAX_ATT_MTU             (156) | #define CFG_BLE_MAX_ATT_MTU             (251) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Size of the storage area for Attribute values |  * Size of the storage area for Attribute values | ||||||
|  | |||||||
| @ -1,367 +0,0 @@ | |||||||
| /* USER CODE BEGIN Header */ |  | ||||||
| /**
 |  | ||||||
|  ****************************************************************************** |  | ||||||
|   * File Name          : app_debug.c |  | ||||||
|   * Description        : Debug capabilities source file for STM32WPAN Middleware |  | ||||||
|  ****************************************************************************** |  | ||||||
|   * @attention |  | ||||||
|   * |  | ||||||
|   * <h2><center>© Copyright (c) 2020 STMicroelectronics. |  | ||||||
|   * All rights reserved.</center></h2> |  | ||||||
|   * |  | ||||||
|   * This software component is licensed by ST under Ultimate Liberty license |  | ||||||
|   * SLA0044, the "License"; You may not use this file except in compliance with |  | ||||||
|   * the License. You may obtain a copy of the License at: |  | ||||||
|   *                             www.st.com/SLA0044 |  | ||||||
|   * |  | ||||||
|  ****************************************************************************** |  | ||||||
|  */ |  | ||||||
| /* USER CODE END Header */ |  | ||||||
| 
 |  | ||||||
| /* Includes ------------------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN Includes */ |  | ||||||
| #include "app_common.h" |  | ||||||
| 
 |  | ||||||
| #include "app_debug.h" |  | ||||||
| #include "utilities_common.h" |  | ||||||
| #include "shci.h" |  | ||||||
| #include "tl.h" |  | ||||||
| #include "dbg_trace.h" |  | ||||||
| #include <furi-hal.h> |  | ||||||
| /* USER CODE END Includes */ |  | ||||||
| 
 |  | ||||||
| /* Private typedef -----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PTD */ |  | ||||||
| typedef PACKED_STRUCT |  | ||||||
| { |  | ||||||
|   GPIO_TypeDef* port; |  | ||||||
|   uint16_t pin; |  | ||||||
|   uint8_t enable; |  | ||||||
|   uint8_t reserved; |  | ||||||
| } APPD_GpioConfig_t; |  | ||||||
| /* USER CODE END PTD */ |  | ||||||
| 
 |  | ||||||
| /* Private defines -----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PD */ |  | ||||||
| #define GPIO_NBR_OF_RF_SIGNALS                  9 |  | ||||||
| #define GPIO_CFG_NBR_OF_FEATURES                34 |  | ||||||
| #define NBR_OF_TRACES_CONFIG_PARAMETERS         4 |  | ||||||
| #define NBR_OF_GENERAL_CONFIG_PARAMETERS        4 |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT |  | ||||||
|  */ |  | ||||||
| #define BLE_DTB_CFG     0 |  | ||||||
| #define SYS_DBG_CFG1  (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)  |  | ||||||
| /* USER CODE END PD */ |  | ||||||
| 
 |  | ||||||
| /* Private variables ---------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PV */ |  | ||||||
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig={0, 0, 0, 0}; |  | ||||||
| PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig={BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT |  | ||||||
|  * It provides timing information on the CPU2 activity. |  | ||||||
|  * All configuration of (port, pin) is supported for each features and can be selected by the user |  | ||||||
|  * depending on the availability |  | ||||||
|  */ |  | ||||||
| static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = |  | ||||||
| { |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_ISR - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_STACK_TICK - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* RNG_PROCESS - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* NVM_PROCESS - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_GENERAL - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* HARD_FAULT - Set on Entry / Reset on Exit */ |  | ||||||
| /* From v1.1.1 */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */ |  | ||||||
| /* From v1.2.0 */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* TIMER_SERVER_CALLBACK - Toggle on Entry */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* PES_ACTIVITY - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ |  | ||||||
| /* From v1.3.0 */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* NVMA_CLEANUP - Set on Entry / Reset on Exit */ |  | ||||||
| /* From v1.4.0 */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* NVMA_START - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* FLASH_EOP - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* FLASH_WRITE - Set on Entry / Reset on Exit */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_0, 0, 0},  /* FLASH_ERASE - Set on Entry / Reset on Exit */ |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT |  | ||||||
|  * This table is relevant only for BLE |  | ||||||
|  * It provides timing information on BLE RF activity. |  | ||||||
|  * New signals may be allocated at any location when requested by ST |  | ||||||
|  * The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed |  | ||||||
|  */ |  | ||||||
| #if( BLE_DTB_CFG == 7) |  | ||||||
| static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = |  | ||||||
| { |  | ||||||
|     { GPIOB, LL_GPIO_PIN_2, 0, 0},      /* DTB10 - Tx/Rx SPI */ |  | ||||||
|     { GPIOB, LL_GPIO_PIN_7, 0, 0},      /* DTB11 - Tx/Tx SPI Clk */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_8, 0, 0},      /* DTB12 - Tx/Rx Ready & SPI Select */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_9, 0, 0},      /* DTB13 - Tx/Rx Start */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_10, 0, 0},     /* DTB14 - FSM0 */ |  | ||||||
|     { GPIOA, LL_GPIO_PIN_11, 0, 0},     /* DTB15 - FSM1 */ |  | ||||||
|     { GPIOB, LL_GPIO_PIN_8, 0, 0},      /* DTB16 - FSM2 */ |  | ||||||
|     { GPIOB, LL_GPIO_PIN_11, 0, 0},     /* DTB17 - FSM3 */ |  | ||||||
|     { GPIOB, LL_GPIO_PIN_10, 0, 0},     /* DTB18 - FSM4 */ |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
| /* USER CODE END PV */ |  | ||||||
| 
 |  | ||||||
| /* Global variables ----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN GV */ |  | ||||||
| /* USER CODE END GV */ |  | ||||||
| 
 |  | ||||||
| /* Private function prototypes -----------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PFP */ |  | ||||||
| static void APPD_SetCPU2GpioConfig( void ); |  | ||||||
| static void APPD_BleDtbCfg( void ); |  | ||||||
| /* USER CODE END PFP */ |  | ||||||
| 
 |  | ||||||
| /* Functions Definition ------------------------------------------------------*/ |  | ||||||
| void APPD_Init( void ) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN APPD_Init */ |  | ||||||
| #if (CFG_DEBUGGER_SUPPORTED == 1) |  | ||||||
|   /**
 |  | ||||||
|    * Keep debugger enabled while in any low power mode |  | ||||||
|    */ |  | ||||||
|   HAL_DBGMCU_EnableDBGSleepMode(); |  | ||||||
|   HAL_DBGMCU_EnableDBGStopMode(); |  | ||||||
| 
 |  | ||||||
|   /***************** ENABLE DEBUGGER *************************************/ |  | ||||||
|   LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); |  | ||||||
| 
 |  | ||||||
| #else |  | ||||||
|   GPIO_InitTypeDef gpio_config = {0}; |  | ||||||
| 
 |  | ||||||
|   gpio_config.Pull = GPIO_NOPULL; |  | ||||||
|   gpio_config.Mode = GPIO_MODE_ANALOG; |  | ||||||
| 
 |  | ||||||
|   gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13; |  | ||||||
|   __HAL_RCC_GPIOA_CLK_ENABLE(); |  | ||||||
|   HAL_GPIO_Init(GPIOA, &gpio_config); |  | ||||||
|   __HAL_RCC_GPIOA_CLK_DISABLE(); |  | ||||||
| 
 |  | ||||||
|   gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3; |  | ||||||
|   __HAL_RCC_GPIOB_CLK_ENABLE(); |  | ||||||
|   HAL_GPIO_Init(GPIOB, &gpio_config); |  | ||||||
|   __HAL_RCC_GPIOB_CLK_DISABLE(); |  | ||||||
| 
 |  | ||||||
|   HAL_DBGMCU_DisableDBGSleepMode(); |  | ||||||
|   HAL_DBGMCU_DisableDBGStopMode(); |  | ||||||
|   HAL_DBGMCU_DisableDBGStandbyMode(); |  | ||||||
| 
 |  | ||||||
| #endif /* (CFG_DEBUGGER_SUPPORTED == 1) */ |  | ||||||
| 
 |  | ||||||
| #if(CFG_DEBUG_TRACE != 0) |  | ||||||
|   DbgTraceInit(); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   APPD_SetCPU2GpioConfig( ); |  | ||||||
|   APPD_BleDtbCfg( ); |  | ||||||
| 
 |  | ||||||
| /* USER CODE END APPD_Init */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void APPD_EnableCPU2( void ) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN APPD_EnableCPU2 */ |  | ||||||
|   SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = |  | ||||||
|   { |  | ||||||
|     {{0,0,0}},                            /**< Does not need to be initialized */ |  | ||||||
|     {(uint8_t *)aGpioConfigList, |  | ||||||
|     (uint8_t *)&APPD_TracesConfig, |  | ||||||
|     (uint8_t *)&APPD_GeneralConfig, |  | ||||||
|     GPIO_CFG_NBR_OF_FEATURES, |  | ||||||
|     NBR_OF_TRACES_CONFIG_PARAMETERS, |  | ||||||
|     NBR_OF_GENERAL_CONFIG_PARAMETERS} |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   /**< Traces channel initialization */ |  | ||||||
|   TL_TRACES_Init( ); |  | ||||||
| 
 |  | ||||||
|   /** GPIO DEBUG Initialization */ |  | ||||||
|   SHCI_C2_DEBUG_Init( &DebugCmdPacket  ); |  | ||||||
| 
 |  | ||||||
| /* USER CODE END APPD_EnableCPU2 */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  * LOCAL FUNCTIONS |  | ||||||
|  * |  | ||||||
|  *************************************************************/ |  | ||||||
| static void APPD_SetCPU2GpioConfig( void ) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN APPD_SetCPU2GpioConfig */ |  | ||||||
|   GPIO_InitTypeDef gpio_config = {0}; |  | ||||||
|   uint8_t local_loop; |  | ||||||
|   uint16_t gpioa_pin_list; |  | ||||||
|   uint16_t gpiob_pin_list; |  | ||||||
|   uint16_t gpioc_pin_list; |  | ||||||
| 
 |  | ||||||
|   gpioa_pin_list = 0; |  | ||||||
|   gpiob_pin_list = 0; |  | ||||||
|   gpioc_pin_list = 0; |  | ||||||
| 
 |  | ||||||
|   for(local_loop = 0 ; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++) |  | ||||||
|   { |  | ||||||
|     if( aGpioConfigList[local_loop].enable != 0) |  | ||||||
|     { |  | ||||||
|       switch((uint32_t)aGpioConfigList[local_loop].port) |  | ||||||
|       { |  | ||||||
|         case (uint32_t)GPIOA: |  | ||||||
|             gpioa_pin_list |= aGpioConfigList[local_loop].pin; |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (uint32_t)GPIOB: |  | ||||||
|             gpiob_pin_list |= aGpioConfigList[local_loop].pin; |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (uint32_t)GPIOC: |  | ||||||
|             gpioc_pin_list |= aGpioConfigList[local_loop].pin; |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   gpio_config.Pull = GPIO_NOPULL; |  | ||||||
|   gpio_config.Mode = GPIO_MODE_OUTPUT_PP; |  | ||||||
|   gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; |  | ||||||
| 
 |  | ||||||
|   if(gpioa_pin_list != 0) |  | ||||||
|   { |  | ||||||
|     gpio_config.Pin = gpioa_pin_list; |  | ||||||
|     __HAL_RCC_GPIOA_CLK_ENABLE(); |  | ||||||
|     __HAL_RCC_C2GPIOA_CLK_ENABLE(); |  | ||||||
|     HAL_GPIO_Init(GPIOA, &gpio_config); |  | ||||||
|     HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(gpiob_pin_list != 0) |  | ||||||
|   { |  | ||||||
|     gpio_config.Pin = gpiob_pin_list; |  | ||||||
|     __HAL_RCC_GPIOB_CLK_ENABLE(); |  | ||||||
|     __HAL_RCC_C2GPIOB_CLK_ENABLE(); |  | ||||||
|     HAL_GPIO_Init(GPIOB, &gpio_config); |  | ||||||
|     HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(gpioc_pin_list != 0) |  | ||||||
|   { |  | ||||||
|     gpio_config.Pin = gpioc_pin_list; |  | ||||||
|     __HAL_RCC_GPIOC_CLK_ENABLE(); |  | ||||||
|     __HAL_RCC_C2GPIOC_CLK_ENABLE(); |  | ||||||
|     HAL_GPIO_Init(GPIOC, &gpio_config); |  | ||||||
|     HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
| /* USER CODE END APPD_SetCPU2GpioConfig */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void APPD_BleDtbCfg( void ) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN APPD_BleDtbCfg */ |  | ||||||
| #if (BLE_DTB_CFG != 0) |  | ||||||
|   GPIO_InitTypeDef gpio_config = {0}; |  | ||||||
|   uint8_t local_loop; |  | ||||||
|   uint16_t gpioa_pin_list; |  | ||||||
|   uint16_t gpiob_pin_list; |  | ||||||
| 
 |  | ||||||
|   gpioa_pin_list = 0; |  | ||||||
|   gpiob_pin_list = 0; |  | ||||||
| 
 |  | ||||||
|   for(local_loop = 0 ; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++) |  | ||||||
|   { |  | ||||||
|     if( aRfConfigList[local_loop].enable != 0) |  | ||||||
|     { |  | ||||||
|       switch((uint32_t)aRfConfigList[local_loop].port) |  | ||||||
|       { |  | ||||||
|         case (uint32_t)GPIOA: |  | ||||||
|             gpioa_pin_list |= aRfConfigList[local_loop].pin; |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (uint32_t)GPIOB: |  | ||||||
|             gpiob_pin_list |= aRfConfigList[local_loop].pin; |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   gpio_config.Pull = GPIO_NOPULL; |  | ||||||
|   gpio_config.Mode = GPIO_MODE_AF_PP; |  | ||||||
|   gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH; |  | ||||||
|   gpio_config.Alternate = GPIO_AF6_RF_DTB7; |  | ||||||
| 
 |  | ||||||
|   if(gpioa_pin_list != 0) |  | ||||||
|   { |  | ||||||
|     gpio_config.Pin = gpioa_pin_list; |  | ||||||
|     __HAL_RCC_GPIOA_CLK_ENABLE(); |  | ||||||
|     __HAL_RCC_C2GPIOA_CLK_ENABLE(); |  | ||||||
|     HAL_GPIO_Init(GPIOA, &gpio_config); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(gpiob_pin_list != 0) |  | ||||||
|   { |  | ||||||
|     gpio_config.Pin = gpiob_pin_list; |  | ||||||
|     __HAL_RCC_GPIOB_CLK_ENABLE(); |  | ||||||
|     __HAL_RCC_C2GPIOB_CLK_ENABLE(); |  | ||||||
|     HAL_GPIO_Init(GPIOB, &gpio_config); |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /* USER CODE END APPD_BleDtbCfg */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  * WRAP FUNCTIONS |  | ||||||
|  * |  | ||||||
| *************************************************************/ |  | ||||||
| #if(CFG_DEBUG_TRACE != 0) |  | ||||||
| void DbgOutputInit( void ) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void DbgOutputTraces(  uint8_t *p_data, uint16_t size, void (*cb)(void) ) |  | ||||||
| { |  | ||||||
|   furi_hal_console_tx(p_data, size); |  | ||||||
|   cb(); |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |  | ||||||
| @ -6,7 +6,6 @@ | |||||||
| #include "tl.h" | #include "tl.h" | ||||||
| #include "cmsis_os.h" | #include "cmsis_os.h" | ||||||
| #include "shci_tl.h" | #include "shci_tl.h" | ||||||
| #include "app_debug.h" |  | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| 
 | 
 | ||||||
| extern RTC_HandleTypeDef hrtc; | extern RTC_HandleTypeDef hrtc; | ||||||
| @ -139,8 +138,10 @@ static void APPE_SysUserEvtRx( void * pPayload ) { | |||||||
|   // APPD_EnableCPU2( );
 |   // APPD_EnableCPU2( );
 | ||||||
|    |    | ||||||
|   if (APP_BLE_Init()) { |   if (APP_BLE_Init()) { | ||||||
|  |     FURI_LOG_I("Core2", "BLE stack started"); | ||||||
|     ble_glue_status = BleGlueStatusStarted; |     ble_glue_status = BleGlueStatusStarted; | ||||||
|   } else { |   } else { | ||||||
|  |     FURI_LOG_E("Core2", "BLE stack startup failed"); | ||||||
|     ble_glue_status = BleGlueStatusBroken; |     ble_glue_status = BleGlueStatusBroken; | ||||||
|   } |   } | ||||||
|   furi_hal_power_insomnia_exit(); |   furi_hal_power_insomnia_exit(); | ||||||
| @ -178,3 +179,16 @@ void shci_cmd_resp_wait(uint32_t timeout) { | |||||||
|   UNUSED(timeout); |   UNUSED(timeout); | ||||||
|   osSemaphoreAcquire( SemShciId, osWaitForever ); |   osSemaphoreAcquire( SemShciId, osWaitForever ); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #if(CFG_DEBUG_TRACE != 0) | ||||||
|  | void DbgOutputInit( void ) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DbgOutputTraces(  uint8_t *p_data, uint16_t size, void (*cb)(void) ) | ||||||
|  | { | ||||||
|  |   furi_hal_console_tx(p_data, size); | ||||||
|  |   cb(); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										78
									
								
								firmware/targets/f6/ble-glue/battery_service.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								firmware/targets/f6/ble-glue/battery_service.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | #include "battery_service.h" | ||||||
|  | #include "app_common.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define BATTERY_SERVICE_TAG "battery service" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t svc_handle; | ||||||
|  |     uint16_t char_level_handle; | ||||||
|  | } BatterySvc; | ||||||
|  | 
 | ||||||
|  | static BatterySvc* battery_svc = NULL; | ||||||
|  | 
 | ||||||
|  | static const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||||
|  | static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||||
|  | 
 | ||||||
|  | void battery_svc_start() { | ||||||
|  |     battery_svc = furi_alloc(sizeof(BatterySvc)); | ||||||
|  |     tBleStatus status; | ||||||
|  | 
 | ||||||
|  |     // Add Battery service
 | ||||||
|  |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); | ||||||
|  |     } | ||||||
|  |     // Add Battery level characteristic
 | ||||||
|  |     status = aci_gatt_add_char(battery_svc->svc_handle, | ||||||
|  |                                 UUID_TYPE_16, | ||||||
|  |                                 (Char_UUID_t *) &char_battery_level_uuid, | ||||||
|  |                                 1, | ||||||
|  |                                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                                 &battery_svc->char_level_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(battery_svc) { | ||||||
|  |         // Delete Battery level characteristic
 | ||||||
|  |         status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete Battery service
 | ||||||
|  |         status = aci_gatt_del_service(battery_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(battery_svc); | ||||||
|  |         battery_svc = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool battery_svc_update_level(uint8_t battery_charge) { | ||||||
|  |     // Check if service was started
 | ||||||
|  |     if(battery_svc == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     // Update battery level characteristic
 | ||||||
|  |     FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); | ||||||
|  |     tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle, | ||||||
|  |                                           battery_svc->char_level_handle, | ||||||
|  |                                           0, | ||||||
|  |                                           1, | ||||||
|  |                                           &battery_charge); | ||||||
|  |     if(result) { | ||||||
|  |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed updating RX characteristic: %d", result); | ||||||
|  |     } | ||||||
|  |     return result != BLE_STATUS_SUCCESS; | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								firmware/targets/f6/ble-glue/battery_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								firmware/targets/f6/ble-glue/battery_service.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void battery_svc_start(); | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop(); | ||||||
|  | 
 | ||||||
|  | bool battery_svc_update_level(uint8_t battery_level); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -53,47 +53,12 @@ | |||||||
| 
 | 
 | ||||||
| #define BLE_CFG_CLT_MAX_NBR_CB                                                 0 | #define BLE_CFG_CLT_MAX_NBR_CB                                                 0 | ||||||
| 
 | 
 | ||||||
| /******************************************************************************
 |  | ||||||
|  * Device Information Service (DIS) |  | ||||||
|  ******************************************************************************/ |  | ||||||
| /**< Options: Supported(1) or Not Supported(0) */ |  | ||||||
| #define BLE_CFG_DIS_MANUFACTURER_NAME_STRING                                   1 |  | ||||||
| #define BLE_CFG_DIS_MODEL_NUMBER_STRING                                        1 |  | ||||||
| #define BLE_CFG_DIS_SERIAL_NUMBER_STRING                                       0 |  | ||||||
| #define BLE_CFG_DIS_HARDWARE_REVISION_STRING                                   0 |  | ||||||
| #define BLE_CFG_DIS_FIRMWARE_REVISION_STRING                                   1 |  | ||||||
| #define BLE_CFG_DIS_SOFTWARE_REVISION_STRING                                   1 |  | ||||||
| #define BLE_CFG_DIS_SYSTEM_ID                                                  0 |  | ||||||
| #define BLE_CFG_DIS_IEEE_CERTIFICATION                                         0 |  | ||||||
| #define BLE_CFG_DIS_PNP_ID                                                     0 |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * device information service characteristic lengths |  | ||||||
|  */ |  | ||||||
| #define BLE_CFG_DIS_SYSTEM_ID_LEN_MAX                                        (8) |  | ||||||
| #define BLE_CFG_DIS_MODEL_NUMBER_STRING_LEN_MAX                              (32) |  | ||||||
| #define BLE_CFG_DIS_SERIAL_NUMBER_STRING_LEN_MAX                             (32) |  | ||||||
| #define BLE_CFG_DIS_FIRMWARE_REVISION_STRING_LEN_MAX                         (32) |  | ||||||
| #define BLE_CFG_DIS_HARDWARE_REVISION_STRING_LEN_MAX                         (32) |  | ||||||
| #define BLE_CFG_DIS_SOFTWARE_REVISION_STRING_LEN_MAX                         (64) |  | ||||||
| #define BLE_CFG_DIS_MANUFACTURER_NAME_STRING_LEN_MAX                         (32) |  | ||||||
| #define BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX                               (32) |  | ||||||
| #define BLE_CFG_DIS_PNP_ID_LEN_MAX                                           (7) |  | ||||||
| 
 |  | ||||||
| /******************************************************************************
 |  | ||||||
|  * Heart Rate Service (HRS) |  | ||||||
|  ******************************************************************************/ |  | ||||||
| #define BLE_CFG_HRS_BODY_SENSOR_LOCATION_CHAR               1/**< BODY SENSOR LOCATION CHARACTERISTIC */ |  | ||||||
| #define BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG               1/**< ENERGY EXTENDED INFO FLAG */ |  | ||||||
| #define BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG                 1/**< Max number of RR interval values - Shall not be greater than 9 */ |  | ||||||
| 
 |  | ||||||
| /******************************************************************************
 | /******************************************************************************
 | ||||||
|  * GAP Service - Apprearance |  * GAP Service - Apprearance | ||||||
|  ******************************************************************************/ |  ******************************************************************************/ | ||||||
| 
 | 
 | ||||||
| #define BLE_CFG_UNKNOWN_APPEARANCE                  (0) | #define BLE_CFG_UNKNOWN_APPEARANCE                  (0) | ||||||
| #define BLE_CFG_HR_SENSOR_APPEARANCE                (832) | #define BLE_CFG_GAP_APPEARANCE                      (0x0086) | ||||||
| #define BLE_CFG_GAP_APPEARANCE                      (BLE_CFG_HR_SENSOR_APPEARANCE) |  | ||||||
| 
 | 
 | ||||||
| /******************************************************************************
 | /******************************************************************************
 | ||||||
|  * Over The Air Feature (OTA) - STM Proprietary |  * Over The Air Feature (OTA) - STM Proprietary | ||||||
|  | |||||||
							
								
								
									
										156
									
								
								firmware/targets/f6/ble-glue/dev_info_service.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								firmware/targets/f6/ble-glue/dev_info_service.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | |||||||
|  | #include "dev_info_service.h" | ||||||
|  | #include "app_common.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define DEV_INFO_SVC_TAG "dev info service" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t service_handle; | ||||||
|  |     uint16_t man_name_char_handle; | ||||||
|  |     uint16_t serial_num_char_handle; | ||||||
|  |     uint16_t firmware_rev_char_handle; | ||||||
|  |     uint16_t software_rev_char_handle; | ||||||
|  | } DevInfoSvc; | ||||||
|  | 
 | ||||||
|  | static DevInfoSvc* dev_info_svc = NULL; | ||||||
|  | 
 | ||||||
|  | static const char dev_info_man_name[] = "Flipper Devices Inc."; | ||||||
|  | static const char dev_info_serial_num[] = "1.0"; | ||||||
|  | static const char dev_info_firmware_rev_num[] = TARGET; | ||||||
|  | static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE; | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_start() { | ||||||
|  |     dev_info_svc = furi_alloc(sizeof(DevInfoSvc)); | ||||||
|  |     tBleStatus status; | ||||||
|  | 
 | ||||||
|  |     // Add Device Information Service
 | ||||||
|  |     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; | ||||||
|  |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Add characteristics
 | ||||||
|  |     uuid = MANUFACTURER_NAME_UUID; | ||||||
|  |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|  |                                 UUID_TYPE_16, | ||||||
|  |                                 (Char_UUID_t*)&uuid, | ||||||
|  |                                 strlen(dev_info_man_name), | ||||||
|  |                                 CHAR_PROP_READ, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                                 &dev_info_svc->man_name_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status); | ||||||
|  |     } | ||||||
|  |     uuid = SERIAL_NUMBER_UUID; | ||||||
|  |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|  |                                 UUID_TYPE_16, | ||||||
|  |                                 (Char_UUID_t*)&uuid, | ||||||
|  |                                 strlen(dev_info_serial_num), | ||||||
|  |                                 CHAR_PROP_READ, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                                 &dev_info_svc->serial_num_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status); | ||||||
|  |     } | ||||||
|  |     uuid = FIRMWARE_REVISION_UUID; | ||||||
|  |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|  |                                 UUID_TYPE_16, | ||||||
|  |                                 (Char_UUID_t*)&uuid, | ||||||
|  |                                 strlen(dev_info_firmware_rev_num), | ||||||
|  |                                 CHAR_PROP_READ, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                                 &dev_info_svc->firmware_rev_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status); | ||||||
|  |     } | ||||||
|  |     uuid = SOFTWARE_REVISION_UUID; | ||||||
|  |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|  |                                 UUID_TYPE_16, | ||||||
|  |                                 (Char_UUID_t*)&uuid, | ||||||
|  |                                 strlen(dev_info_software_rev_num), | ||||||
|  |                                 CHAR_PROP_READ, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                                 &dev_info_svc->software_rev_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Update characteristics
 | ||||||
|  |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|  |                                         dev_info_svc->man_name_char_handle, | ||||||
|  |                                         0, | ||||||
|  |                                         strlen(dev_info_man_name), | ||||||
|  |                                         (uint8_t*)dev_info_man_name); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|  |                                         dev_info_svc->serial_num_char_handle, | ||||||
|  |                                         0, | ||||||
|  |                                         strlen(dev_info_serial_num), | ||||||
|  |                                         (uint8_t*)dev_info_serial_num); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|  |                                         dev_info_svc->firmware_rev_char_handle, | ||||||
|  |                                         0, | ||||||
|  |                                         strlen(dev_info_firmware_rev_num), | ||||||
|  |                                         (uint8_t*)dev_info_firmware_rev_num); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|  |                                         dev_info_svc->software_rev_char_handle, | ||||||
|  |                                         0, | ||||||
|  |                                         strlen(dev_info_software_rev_num), | ||||||
|  |                                         (uint8_t*)dev_info_software_rev_num); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(dev_info_svc) { | ||||||
|  |         // Delete service characteristics
 | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(dev_info_svc->service_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(dev_info_svc); | ||||||
|  |         dev_info_svc = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								firmware/targets/f6/ble-glue/dev_info_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								firmware/targets/f6/ble-glue/dev_info_service.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define DEV_INFO_MANUFACTURER_NAME              "Flipper Devices Inc." | ||||||
|  | #define DEV_INFO_SERIAL_NUMBER                  "1.0" | ||||||
|  | #define DEV_INFO_FIRMWARE_REVISION_NUMBER       TARGET | ||||||
|  | #define DEV_INFO_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_start(); | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_stop(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,157 +0,0 @@ | |||||||
| #include "app_common.h" |  | ||||||
| #include "ble.h" |  | ||||||
| #include "dis_app.h" |  | ||||||
| #include <furi-hal-version.h> |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
| static const uint8_t system_id[BLE_CFG_DIS_SYSTEM_ID_LEN_MAX] = { |  | ||||||
|   (uint8_t)((DISAPP_MANUFACTURER_ID & 0xFF0000) >> 16), |  | ||||||
|   (uint8_t)((DISAPP_MANUFACTURER_ID & 0x00FF00) >> 8), |  | ||||||
|   (uint8_t)(DISAPP_MANUFACTURER_ID & 0x0000FF), |  | ||||||
|   0xFE, |  | ||||||
|   0xFF, |  | ||||||
|   (uint8_t)((DISAPP_OUI & 0xFF0000) >> 16), |  | ||||||
|   (uint8_t)((DISAPP_OUI & 0x00FF00) >> 8), |  | ||||||
|   (uint8_t)(DISAPP_OUI & 0x0000FF) |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
| static const uint8_t ieee_id[BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX] = { |  | ||||||
|   0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, |  | ||||||
|   0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, |  | ||||||
|   0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, |  | ||||||
|   0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
| static const uint8_t pnp_id[BLE_CFG_DIS_PNP_ID_LEN_MAX] = { |  | ||||||
|   0x1, |  | ||||||
|   0xAD, 0xDE, |  | ||||||
|   0xDE, 0xDA, |  | ||||||
|   0x01, 0x00 |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| void DISAPP_Init(void) { |  | ||||||
|   DIS_Data_t dis_information_data; |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_MANUFACTURER_NAME_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update MANUFACTURER NAME Information |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)DISAPP_MANUFACTURER_NAME; |  | ||||||
|   dis_information_data.Length = sizeof(DISAPP_MANUFACTURER_NAME); |  | ||||||
|   DIS_UpdateChar(MANUFACTURER_NAME_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_MODEL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update MODEL NUMBERInformation |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   const char* name = furi_hal_version_get_device_name_ptr(); |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)name; |  | ||||||
|   dis_information_data.Length = strlen(name) + 1; |  | ||||||
|   DIS_UpdateChar(MODEL_NUMBER_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_SERIAL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update SERIAL NUMBERInformation |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)DISAPP_SERIAL_NUMBER; |  | ||||||
|   dis_information_data.Length = sizeof(DISAPP_SERIAL_NUMBER); |  | ||||||
|   DIS_UpdateChar(SERIAL_NUMBER_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_HARDWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update HARDWARE REVISION NUMBERInformation |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)DISAPP_HARDWARE_REVISION_NUMBER; |  | ||||||
|   dis_information_data.Length = sizeof(DISAPP_HARDWARE_REVISION_NUMBER); |  | ||||||
|   DIS_UpdateChar(HARDWARE_REVISION_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_FIRMWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update FIRMWARE REVISION NUMBERInformation |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)DISAPP_FIRMWARE_REVISION_NUMBER; |  | ||||||
|   dis_information_data.Length = sizeof(DISAPP_FIRMWARE_REVISION_NUMBER); |  | ||||||
|   DIS_UpdateChar(FIRMWARE_REVISION_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_SOFTWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update SOFTWARE REVISION NUMBERInformation |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t*)DISAPP_SOFTWARE_REVISION_NUMBER; |  | ||||||
|   dis_information_data.Length = sizeof(DISAPP_SOFTWARE_REVISION_NUMBER); |  | ||||||
|   DIS_UpdateChar(SOFTWARE_REVISION_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update SYSTEM ID Information |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t *)system_id; |  | ||||||
|   dis_information_data.Length = BLE_CFG_DIS_SYSTEM_ID_LEN_MAX; |  | ||||||
|   DIS_UpdateChar(SYSTEM_ID_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update IEEE CERTIFICATION ID Information |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t *)ieee_id; |  | ||||||
|   dis_information_data.Length = BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX; |  | ||||||
|   DIS_UpdateChar(IEEE_CERTIFICATION_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0)) |  | ||||||
|   /**
 |  | ||||||
|    * Update PNP ID Information |  | ||||||
|    * |  | ||||||
|    * @param UUID |  | ||||||
|    * @param pPData |  | ||||||
|    * @return |  | ||||||
|    */ |  | ||||||
|   dis_information_data.pPayload = (uint8_t *)pnp_id; |  | ||||||
|   dis_information_data.Length = BLE_CFG_DIS_PNP_ID_LEN_MAX; |  | ||||||
|   DIS_UpdateChar(PNP_ID_UUID, &dis_information_data); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #define DISAPP_MANUFACTURER_NAME              "Flipperdevice Inc." |  | ||||||
| //#define DISAPP_MODEL_NUMBER                   "FlipperZero"
 |  | ||||||
| #define DISAPP_SERIAL_NUMBER                  "1.0" |  | ||||||
| #define DISAPP_HARDWARE_REVISION_NUMBER       "1.0" |  | ||||||
| #define DISAPP_FIRMWARE_REVISION_NUMBER       TARGET |  | ||||||
| #define DISAPP_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE |  | ||||||
| #define DISAPP_OUI                            0x123456 |  | ||||||
| #define DISAPP_MANUFACTURER_ID                0x9ABCDE |  | ||||||
| 
 |  | ||||||
| void DISAPP_Init(void); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
							
								
								
									
										387
									
								
								firmware/targets/f6/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								firmware/targets/f6/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,387 @@ | |||||||
|  | #include "gap.h" | ||||||
|  | 
 | ||||||
|  | #include "app_entry.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include "cmsis_os.h" | ||||||
|  | #include "otp.h" | ||||||
|  | #include "dev_info_service.h" | ||||||
|  | #include "battery_service.h" | ||||||
|  | #include "serial_service.h" | ||||||
|  | 
 | ||||||
|  | #include <applications/bt/bt_service/bt.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | 
 | ||||||
|  | #define GAP_TAG "BLE" | ||||||
|  | 
 | ||||||
|  | #define FAST_ADV_TIMEOUT 30000 | ||||||
|  | #define INITIAL_ADV_TIMEOUT 60000 | ||||||
|  | 
 | ||||||
|  | #define BD_ADDR_SIZE_LOCAL 6 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   uint16_t gap_svc_handle; | ||||||
|  |   uint16_t dev_name_char_handle; | ||||||
|  |   uint16_t appearance_char_handle; | ||||||
|  |   uint16_t connection_handle; | ||||||
|  |   uint8_t adv_svc_uuid_len; | ||||||
|  |   uint8_t adv_svc_uuid[20]; | ||||||
|  | } GapSvc; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   GapSvc gap_svc; | ||||||
|  |   GapState state; | ||||||
|  |   uint8_t mac_address[BD_ADDR_SIZE_LOCAL]; | ||||||
|  |   Bt* bt; | ||||||
|  |   osTimerId advertise_timer; | ||||||
|  |   osThreadAttr_t thread_attr; | ||||||
|  |   osThreadId_t thread_id; | ||||||
|  | } Gap; | ||||||
|  | 
 | ||||||
|  | // Identity root key
 | ||||||
|  | static const uint8_t gap_irk[16] = {0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; | ||||||
|  | // Encryption root key
 | ||||||
|  | static const uint8_t gap_erk[16] = {0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21,0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21}; | ||||||
|  | // Appearence characteristic UUID
 | ||||||
|  | static const uint8_t gap_appearence_char_uuid[] = {0x00, 0x86}; | ||||||
|  | // Default MAC address
 | ||||||
|  | static const uint8_t gap_default_mac_addr[] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; | ||||||
|  | 
 | ||||||
|  | static Gap* gap = NULL; | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state); | ||||||
|  | static void gap_app(void *arg); | ||||||
|  | 
 | ||||||
|  | SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) | ||||||
|  | { | ||||||
|  |   hci_event_pckt *event_pckt; | ||||||
|  |   evt_le_meta_event *meta_evt; | ||||||
|  |   evt_blue_aci *blue_evt; | ||||||
|  |   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; | ||||||
|  |   uint8_t tx_phy; | ||||||
|  |   uint8_t rx_phy; | ||||||
|  |   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; | ||||||
|  | 
 | ||||||
|  |   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; | ||||||
|  | 
 | ||||||
|  |   switch (event_pckt->evt) { | ||||||
|  |     case EVT_DISCONN_COMPLETE: | ||||||
|  |     { | ||||||
|  |         hci_disconnection_complete_event_rp0 *disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; | ||||||
|  |         if (disconnection_complete_event->Connection_Handle == gap->gap_svc.connection_handle) { | ||||||
|  |             gap->gap_svc.connection_handle = 0; | ||||||
|  |             gap->state = GapStateIdle; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Disconnect from client"); | ||||||
|  |         } | ||||||
|  |         // Restart advertising
 | ||||||
|  |         gap_advertise(GapStateAdvFast); | ||||||
|  |         furi_hal_power_insomnia_exit(); | ||||||
|  |     } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_LE_META_EVENT: | ||||||
|  |         meta_evt = (evt_le_meta_event*) event_pckt->data; | ||||||
|  |         switch (meta_evt->subevent) { | ||||||
|  |             case EVT_LE_CONN_UPDATE_COMPLETE: | ||||||
|  |             FURI_LOG_D(GAP_TAG, "Connection update event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_PHY_UPDATE_COMPLETE: | ||||||
|  |             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; | ||||||
|  |             if(evt_le_phy_update_complete->Status) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "Update PHY succeed"); | ||||||
|  |             } | ||||||
|  |             ret = hci_le_read_phy(gap->gap_svc.connection_handle,&tx_phy,&rx_phy); | ||||||
|  |             if(ret) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Read PHY failed, status: %d", ret); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "PHY Params TX= %d, RX= %d ", tx_phy, rx_phy); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_CONN_COMPLETE: | ||||||
|  |             furi_hal_power_insomnia_enter(); | ||||||
|  |             hci_le_connection_complete_event_rp0* connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Connection complete for connection handle 0x%x", connection_complete_event->Connection_Handle); | ||||||
|  | 
 | ||||||
|  |             // Stop advertising as connection completed
 | ||||||
|  |             osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |             // Update connection status and handle
 | ||||||
|  |             gap->state = GapStateConnected; | ||||||
|  |             gap->gap_svc.connection_handle = connection_complete_event->Connection_Handle; | ||||||
|  | 
 | ||||||
|  |             // Start pairing by sending security request
 | ||||||
|  |             aci_gap_slave_security_req(connection_complete_event->Connection_Handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_VENDOR: | ||||||
|  |         blue_evt = (evt_blue_aci*) event_pckt->data; | ||||||
|  |         switch (blue_evt->ecode) { | ||||||
|  |             aci_gap_pairing_complete_event_rp0 *pairing_complete; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Limited discoverable event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PASS_KEY_REQUEST: | ||||||
|  |         { | ||||||
|  |             // Generate random PIN code
 | ||||||
|  |             uint32_t pin = rand() % 999999; | ||||||
|  |             aci_gap_pass_key_resp(gap->gap_svc.connection_handle, pin); | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pass key request event. Pin: %d", pin); | ||||||
|  |             bt_pin_code_show(gap->bt, pin); | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Authorization request event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Slave security initiated"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_BOND_LOST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Bond lost event. Start rebonding"); | ||||||
|  |             aci_gap_allow_rebond(gap->gap_svc.connection_handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_DEVICE_FOUND: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Device found event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Address not resolved event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Key press notification event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Hex_value = %lx", | ||||||
|  |                         ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); | ||||||
|  |             aci_gap_numeric_comparison_value_confirm_yesno(gap->gap_svc.connection_handle, 1); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case (EVT_BLUE_GAP_PAIRING_CMPLT): | ||||||
|  |         { | ||||||
|  |             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; | ||||||
|  |             if (pairing_complete->Status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Pairing failed with status: %d. Terminating connection", pairing_complete->Status); | ||||||
|  |             aci_gap_terminate(gap->gap_svc.connection_handle, 5); | ||||||
|  |             } else { | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pairing complete"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Procedure complete event"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return SVCCTL_UserEvtFlowEnable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SVCCTL_SvcInit() { | ||||||
|  |     // Dummy function to prevent unused services initialization
 | ||||||
|  |     // TODO refactor (disable all services in WPAN config)
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len = 1; | ||||||
|  |     if(uid_len == 2) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID; | ||||||
|  |     } else if (uid_len == 4) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID; | ||||||
|  |     } else if(uid_len == 16) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST; | ||||||
|  |     } | ||||||
|  |     memcpy(&gap->gap_svc.adv_svc_uuid[1], uid, uid_len); | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len += uid_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status() { | ||||||
|  |     return gap->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gap_init_mac_address(Gap* gap) { | ||||||
|  |     uint8_t *otp_addr; | ||||||
|  |     uint32_t udn; | ||||||
|  |     uint32_t company_id; | ||||||
|  |     uint32_t device_id; | ||||||
|  | 
 | ||||||
|  |     udn = LL_FLASH_GetUDN(); | ||||||
|  |     if(udn != 0xFFFFFFFF) { | ||||||
|  |         company_id = LL_FLASH_GetSTCompanyID(); | ||||||
|  |         device_id = LL_FLASH_GetDeviceID(); | ||||||
|  |         gap->mac_address[0] = (uint8_t)(udn & 0x000000FF); | ||||||
|  |         gap->mac_address[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); | ||||||
|  |         gap->mac_address[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); | ||||||
|  |         gap->mac_address[3] = (uint8_t)device_id; | ||||||
|  |         gap->mac_address[4] = (uint8_t)(company_id & 0x000000FF);; | ||||||
|  |         gap->mac_address[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); | ||||||
|  |     } else { | ||||||
|  |         otp_addr = OTP_Read(0); | ||||||
|  |         if(otp_addr) { | ||||||
|  |             memcpy(gap->mac_address, ((OTP_ID0_t*)otp_addr)->bd_address, sizeof(gap->mac_address)); | ||||||
|  |         } else { | ||||||
|  |             memcpy(gap->mac_address, gap_default_mac_addr, sizeof(gap->mac_address)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_init_svc(Gap* gap) { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint32_t srd_bd_addr[2]; | ||||||
|  | 
 | ||||||
|  |     //HCI Reset to synchronise BLE Stack*/
 | ||||||
|  |     hci_reset(); | ||||||
|  |     // Configure mac address
 | ||||||
|  |     gap_init_mac_address(gap); | ||||||
|  |     aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t*)gap->mac_address); | ||||||
|  | 
 | ||||||
|  |     /* Static random Address
 | ||||||
|  |      * The two upper bits shall be set to 1 | ||||||
|  |      * The lowest 32bits is read from the UDN to differentiate between devices | ||||||
|  |      * The RNG may be used to provide a random number on each power on | ||||||
|  |      */ | ||||||
|  |     srd_bd_addr[1] = 0x0000ED6E; | ||||||
|  |     srd_bd_addr[0] = LL_FLASH_GetUDN(); | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); | ||||||
|  |     // Set Identity root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk ); | ||||||
|  |     // Set Encryption root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk ); | ||||||
|  |     // Set TX Power to 0 dBm
 | ||||||
|  |     aci_hal_set_tx_power_level(1, 0x19); | ||||||
|  |     // Initialize GATT interface
 | ||||||
|  |     aci_gatt_init(); | ||||||
|  |     // Initialize GAP interface
 | ||||||
|  |     const char *name = furi_hal_version_get_device_name_ptr(); | ||||||
|  |     aci_gap_init(GAP_PERIPHERAL_ROLE, 0, strlen(name), | ||||||
|  |                 &gap->gap_svc.gap_svc_handle, &gap->gap_svc.dev_name_char_handle, &gap->gap_svc.appearance_char_handle); | ||||||
|  | 
 | ||||||
|  |     // Set GAP characteristics
 | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.dev_name_char_handle, 0, strlen(name), (uint8_t *) name); | ||||||
|  |     if (status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating name characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.appearance_char_handle, 0, 2, gap_appearence_char_uuid); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating appearence characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     // Set default PHY
 | ||||||
|  |     hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED); | ||||||
|  |     // Set I/O capability
 | ||||||
|  |     aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); | ||||||
|  |     // Setup  authentication
 | ||||||
|  |     aci_gap_set_authentication_requirement(1, 1, 1, 0, 8, 16, 1, 0, PUBLIC_ADDR); | ||||||
|  |     // Configure whitelist
 | ||||||
|  |     aci_gap_configure_whitelist(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state) | ||||||
|  | { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint16_t min_interval; | ||||||
|  |     uint16_t max_interval; | ||||||
|  | 
 | ||||||
|  |     if (new_state == GapStateAdvFast) { | ||||||
|  |         min_interval = 0x80; // 80 ms
 | ||||||
|  |         max_interval = 0xa0; // 100 ms
 | ||||||
|  |     } else { | ||||||
|  |         min_interval = 0x0640; // 1 s
 | ||||||
|  |         max_interval = 0x0fa0; // 2.5 s
 | ||||||
|  |     } | ||||||
|  |     // Stop advertising timer
 | ||||||
|  |     osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |     if ((new_state == GapStateAdvLowPower) && ((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) { | ||||||
|  |         // Stop advertising
 | ||||||
|  |         status = aci_gap_set_non_discoverable(); | ||||||
|  |         if (status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Stop Advertising Failed, result: %d", status); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Configure advertising
 | ||||||
|  |     gap->state = new_state; | ||||||
|  |     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); | ||||||
|  |     status = aci_gap_set_discoverable(ADV_IND, min_interval, max_interval, PUBLIC_ADDR, 0, | ||||||
|  |                                         strlen(name), (uint8_t*)name, | ||||||
|  |                                         gap->gap_svc.adv_svc_uuid_len, gap->gap_svc.adv_svc_uuid, 0, 0); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Set discoverable err: %d", status); | ||||||
|  |     } | ||||||
|  |     osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise_request(Gap* gap) { | ||||||
|  |     osThreadFlagsSet(gap->thread_id, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advetise_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Gap* gap = context; | ||||||
|  |     gap_advertise_request(gap); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool gap_init() { | ||||||
|  |     if (APPE_Status() != BleGlueStatusStarted) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     gap = furi_alloc(sizeof(Gap)); | ||||||
|  |     srand(DWT->CYCCNT); | ||||||
|  |     // Open Bt record
 | ||||||
|  |     gap->bt = furi_record_open("bt"); | ||||||
|  |     // Create advertising timer
 | ||||||
|  |     gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, &gap, NULL); | ||||||
|  |     // Initialization of HCI & GATT & GAP layer
 | ||||||
|  |     gap_init_svc(gap); | ||||||
|  |     // Initialization of the BLE Services
 | ||||||
|  |     SVCCTL_Init(); | ||||||
|  |     // Initialization of the BLE App Context
 | ||||||
|  |     gap->state = GapStateIdle; | ||||||
|  |     gap->gap_svc.connection_handle = 0xFFFF; | ||||||
|  | 
 | ||||||
|  |     // Thread configuration
 | ||||||
|  |     gap->thread_attr.name = "BLE advertising"; | ||||||
|  |     gap->thread_attr.stack_size = 512; | ||||||
|  |     gap->thread_id = osThreadNew(gap_app, NULL, &gap->thread_attr); | ||||||
|  | 
 | ||||||
|  |     // Start Device Information service
 | ||||||
|  |     dev_info_svc_start(); | ||||||
|  |     // Start Battery service
 | ||||||
|  |     battery_svc_start(); | ||||||
|  |     // Start Serial application
 | ||||||
|  |     serial_svc_start(); | ||||||
|  |     // Configure advirtise service UUID
 | ||||||
|  |     uint8_t adv_service_uid[2]; | ||||||
|  |     adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color(); | ||||||
|  |     adv_service_uid[1] = 0x30; | ||||||
|  | 
 | ||||||
|  |     set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); | ||||||
|  |     gap_advertise(GapStateAdvFast); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_app(void *arg) { | ||||||
|  |     // TODO Exit from app, stop service, clean memory
 | ||||||
|  |     while(1) { | ||||||
|  |         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); | ||||||
|  |         gap_advertise(GapStateAdvLowPower); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								firmware/targets/f6/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								firmware/targets/f6/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     GapStateIdle, | ||||||
|  |     GapStateAdvFast, | ||||||
|  |     GapStateAdvLowPower, | ||||||
|  |     GapStateConnected, | ||||||
|  | } GapState; | ||||||
|  | 
 | ||||||
|  | bool gap_init(); | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -1,256 +0,0 @@ | |||||||
| /* USER CODE BEGIN Header */ |  | ||||||
| /**
 |  | ||||||
|   ****************************************************************************** |  | ||||||
|   * @file    hrs_app.c |  | ||||||
|   * @author  MCD Application Team |  | ||||||
|   * @brief   Heart Rate Service Application |  | ||||||
|   ****************************************************************************** |  | ||||||
|   * @attention |  | ||||||
|  * |  | ||||||
|   * <h2><center>© Copyright (c) 2019 STMicroelectronics.  |  | ||||||
|  * All rights reserved.</center></h2> |  | ||||||
|  * |  | ||||||
|   * This software component is licensed by ST under Ultimate Liberty license  |  | ||||||
|   * SLA0044, the "License"; You may not use this file except in compliance with  |  | ||||||
|  * the License. You may obtain a copy of the License at: |  | ||||||
|  *                             www.st.com/SLA0044 |  | ||||||
|  * |  | ||||||
|  ****************************************************************************** |  | ||||||
|  */ |  | ||||||
| /* USER CODE END Header */ |  | ||||||
| 
 |  | ||||||
| /* Includes ------------------------------------------------------------------*/ |  | ||||||
| #include "app_common.h" |  | ||||||
| 
 |  | ||||||
| #include "ble.h" |  | ||||||
| #include "hrs_app.h" |  | ||||||
| #include "cmsis_os.h" |  | ||||||
| 
 |  | ||||||
| /* Private includes ----------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN Includes */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END Includes */ |  | ||||||
| 
 |  | ||||||
| /* Private typedef -----------------------------------------------------------*/ |  | ||||||
| typedef struct |  | ||||||
| { |  | ||||||
|   HRS_BodySensorLocation_t BodySensorLocationChar; |  | ||||||
|   HRS_MeasVal_t MeasurementvalueChar; |  | ||||||
|   uint8_t ResetEnergyExpended; |  | ||||||
|   uint8_t TimerMeasurement_Id; |  | ||||||
| 
 |  | ||||||
| } HRSAPP_Context_t; |  | ||||||
| /* USER CODE BEGIN PTD */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END PTD */ |  | ||||||
| 
 |  | ||||||
| /* Private defines ------------------------------------------------------------*/ |  | ||||||
| /* USER CODE BEGIN PD */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END PD */ |  | ||||||
| 
 |  | ||||||
| /* Private macros ------------------------------------------------------------*/ |  | ||||||
| #define HRSAPP_MEASUREMENT_INTERVAL   (1000000/CFG_TS_TICK_VAL)  /**< 1s */ |  | ||||||
| /* USER CODE BEGIN PM */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END PM */ |  | ||||||
| 
 |  | ||||||
| /* Private variables ---------------------------------------------------------*/ |  | ||||||
| /**
 |  | ||||||
|  * START of Section BLE_APP_CONTEXT |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static HRSAPP_Context_t HRSAPP_Context; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * END of Section BLE_APP_CONTEXT |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| osThreadId_t HrsProcessId; |  | ||||||
| 
 |  | ||||||
| const osThreadAttr_t HrsProcess_attr = { |  | ||||||
|     .name = CFG_HRS_PROCESS_NAME, |  | ||||||
|     .attr_bits = CFG_HRS_PROCESS_ATTR_BITS, |  | ||||||
|     .cb_mem = CFG_HRS_PROCESS_CB_MEM, |  | ||||||
|     .cb_size = CFG_HRS_PROCESS_CB_SIZE, |  | ||||||
|     .stack_mem = CFG_HRS_PROCESS_STACK_MEM, |  | ||||||
|     .priority = CFG_HRS_PROCESS_PRIORITY, |  | ||||||
|     .stack_size = CFG_HRS_PROCESS_STACK_SIZE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* USER CODE BEGIN PV */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END PV */ |  | ||||||
| 
 |  | ||||||
| /* Private functions prototypes-----------------------------------------------*/ |  | ||||||
| static void HrMeas( void ); |  | ||||||
| static void HrsProcess(void *argument); |  | ||||||
| static void HRSAPP_Measurement(void); |  | ||||||
| static uint32_t HRSAPP_Read_RTC_SSR_SS ( void ); |  | ||||||
| /* USER CODE BEGIN PFP */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END PFP */ |  | ||||||
| 
 |  | ||||||
| /* Functions Definition ------------------------------------------------------*/ |  | ||||||
| void HRS_Notification(HRS_App_Notification_evt_t *pNotification) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN HRS_Notification_1 */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRS_Notification_1 */ |  | ||||||
|   switch(pNotification->HRS_Evt_Opcode) |  | ||||||
|   { |  | ||||||
| /* USER CODE BEGIN HRS_Notification_HRS_Evt_Opcode */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRS_Notification_HRS_Evt_Opcode */ |  | ||||||
| #if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0) |  | ||||||
|     case HRS_RESET_ENERGY_EXPENDED_EVT: |  | ||||||
| /* USER CODE BEGIN HRS_RESET_ENERGY_EXPENDED_EVT */ |  | ||||||
|       HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 0; |  | ||||||
|       HRSAPP_Context.ResetEnergyExpended = 1; |  | ||||||
| /* USER CODE END HRS_RESET_ENERGY_EXPENDED_EVT */ |  | ||||||
|       break; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     case HRS_NOTIFICATION_ENABLED: |  | ||||||
| /* USER CODE BEGIN HRS_NOTIFICATION_ENABLED */ |  | ||||||
|       /**
 |  | ||||||
|        * It could be the enable notification is received twice without the disable notification in between |  | ||||||
|        */ |  | ||||||
|       HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id); |  | ||||||
|       HW_TS_Start(HRSAPP_Context.TimerMeasurement_Id, HRSAPP_MEASUREMENT_INTERVAL); |  | ||||||
| /* USER CODE END HRS_NOTIFICATION_ENABLED */ |  | ||||||
|       break; |  | ||||||
| 
 |  | ||||||
|     case HRS_NOTIFICATION_DISABLED: |  | ||||||
| /* USER CODE BEGIN HRS_NOTIFICATION_DISABLED */ |  | ||||||
|       HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id); |  | ||||||
| /* USER CODE END HRS_NOTIFICATION_DISABLED */ |  | ||||||
|       break; |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_OTA_REBOOT_CHAR != 0) |  | ||||||
|     case HRS_STM_BOOT_REQUEST_EVT: |  | ||||||
| /* USER CODE BEGIN HRS_STM_BOOT_REQUEST_EVT */ |  | ||||||
|       *(uint32_t*)SRAM1_BASE = *(uint32_t*)pNotification->DataTransfered.pPayload; |  | ||||||
|       NVIC_SystemReset(); |  | ||||||
| /* USER CODE END HRS_STM_BOOT_REQUEST_EVT */ |  | ||||||
|       break; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|    default: |  | ||||||
| /* USER CODE BEGIN HRS_Notification_Default */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRS_Notification_Default */ |  | ||||||
|       break; |  | ||||||
|   } |  | ||||||
| /* USER CODE BEGIN HRS_Notification_2 */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRS_Notification_2 */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void HRSAPP_Init(void) |  | ||||||
| { |  | ||||||
|   HrsProcessId = osThreadNew(HrsProcess, NULL, &HrsProcess_attr); |  | ||||||
| /* USER CODE BEGIN HRSAPP_Init */ |  | ||||||
|   /**
 |  | ||||||
|    * Set Body Sensor Location |  | ||||||
|    */ |  | ||||||
|   HRSAPP_Context.ResetEnergyExpended = 0; |  | ||||||
|   HRSAPP_Context.BodySensorLocationChar = HRS_BODY_SENSOR_LOCATION_HAND; |  | ||||||
|   HRS_UpdateChar(SENSOR_LOCATION_UUID, (uint8_t *)&HRSAPP_Context.BodySensorLocationChar); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Set Flags for measurement value |  | ||||||
|    */ |  | ||||||
| 
 |  | ||||||
|   HRSAPP_Context.MeasurementvalueChar.Flags = ( HRS_HRM_VALUE_FORMAT_UINT16      |  |  | ||||||
|                                                   HRS_HRM_SENSOR_CONTACTS_PRESENT   |  |  | ||||||
|                                                   HRS_HRM_SENSOR_CONTACTS_SUPPORTED | |  | ||||||
|                                                   HRS_HRM_ENERGY_EXPENDED_PRESENT  | |  | ||||||
|                                                   HRS_HRM_RR_INTERVAL_PRESENT ); |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0) |  | ||||||
|   if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT) |  | ||||||
|     HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 10; |  | ||||||
| #endif |  | ||||||
|    |  | ||||||
| #if (BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG != 0) |  | ||||||
|   if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_RR_INTERVAL_PRESENT) |  | ||||||
|   { |  | ||||||
|     uint8_t i; |  | ||||||
|      |  | ||||||
|     HRSAPP_Context.MeasurementvalueChar.NbreOfValidRRIntervalValues = BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG; |  | ||||||
|     for(i = 0; i < BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG; i++) |  | ||||||
|       HRSAPP_Context.MeasurementvalueChar.aRRIntervalValues[i] = 1024; |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|    |  | ||||||
|   /**
 |  | ||||||
|    * Create timer for Heart Rate Measurement |  | ||||||
|    */ |  | ||||||
|   HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(HRSAPP_Context.TimerMeasurement_Id), hw_ts_Repeated, HrMeas); |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRSAPP_Init */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HrsProcess(void *argument) |  | ||||||
| { |  | ||||||
|   UNUSED(argument); |  | ||||||
| 
 |  | ||||||
|   for(;;) |  | ||||||
|   { |  | ||||||
|     osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever); |  | ||||||
|     HRSAPP_Measurement( ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HRSAPP_Measurement(void) |  | ||||||
| { |  | ||||||
| /* USER CODE BEGIN HRSAPP_Measurement */ |  | ||||||
|   uint32_t measurement; |  | ||||||
| 
 |  | ||||||
|   measurement = ((HRSAPP_Read_RTC_SSR_SS()) & 0x07) + 65; |  | ||||||
| 
 |  | ||||||
|   HRSAPP_Context.MeasurementvalueChar.MeasurementValue = measurement; |  | ||||||
| #if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0) |  | ||||||
|   if((HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT) && |  | ||||||
|      (HRSAPP_Context.ResetEnergyExpended == 0)) |  | ||||||
|     HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 5; |  | ||||||
|   else if(HRSAPP_Context.ResetEnergyExpended == 1) |  | ||||||
|     HRSAPP_Context.ResetEnergyExpended = 0; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   HRS_UpdateChar(HEART_RATE_MEASURMENT_UUID, (uint8_t *)&HRSAPP_Context.MeasurementvalueChar); |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HRSAPP_Measurement */ |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HrMeas( void ) |  | ||||||
| { |  | ||||||
|   /**
 |  | ||||||
|    * The code shall be executed in the background as aci command may be sent |  | ||||||
|    * The background is the only place where the application can make sure a new aci command |  | ||||||
|    * is not sent if there is a pending one |  | ||||||
|    */ |  | ||||||
|   osThreadFlagsSet( HrsProcessId, 1 ); |  | ||||||
| 
 |  | ||||||
| /* USER CODE BEGIN HrMeas */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END HrMeas */ |  | ||||||
| 
 |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static uint32_t HRSAPP_Read_RTC_SSR_SS ( void ) |  | ||||||
| { |  | ||||||
|   return ((uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* USER CODE BEGIN FD */ |  | ||||||
| 
 |  | ||||||
| /* USER CODE END FD */ |  | ||||||
| 
 |  | ||||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |  | ||||||
							
								
								
									
										122
									
								
								firmware/targets/f6/ble-glue/serial_service.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								firmware/targets/f6/ble-glue/serial_service.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | #include "serial_service.h" | ||||||
|  | #include "app_common.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define SERIAL_SERVICE_TAG "serial service" | ||||||
|  | 
 | ||||||
|  | #define SERIAL_SVC_DATA_LEN_MAX 245 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t svc_handle; | ||||||
|  |     uint16_t rx_char_handle; | ||||||
|  |     uint16_t tx_char_handle; | ||||||
|  | } SerialSvc; | ||||||
|  | 
 | ||||||
|  | static SerialSvc* serial_svc; | ||||||
|  | 
 | ||||||
|  | static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; | ||||||
|  | static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
|  | static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
|  | 
 | ||||||
|  | static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | ||||||
|  |     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; | ||||||
|  |     hci_event_pckt* event_pckt = (hci_event_pckt *)(((hci_uart_pckt*)event)->data); | ||||||
|  |     evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; | ||||||
|  |     aci_gatt_attribute_modified_event_rp0* attribute_modified; | ||||||
|  |     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||||
|  |         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||||
|  |             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; | ||||||
|  |             if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) { | ||||||
|  |                 // Descriptor handle
 | ||||||
|  |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|  |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); | ||||||
|  |             } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) { | ||||||
|  |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||||
|  |                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); | ||||||
|  |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|  |             } | ||||||
|  |         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { | ||||||
|  |             FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); | ||||||
|  |             ret = SVCCTL_EvtAckFlowEnable; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void serial_svc_start() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     serial_svc = furi_alloc(sizeof(SerialSvc)); | ||||||
|  |     // Register event handler
 | ||||||
|  |     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||||
|  | 
 | ||||||
|  |     // Add service
 | ||||||
|  |     status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Add TX characteristics
 | ||||||
|  |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid, | ||||||
|  |                                 SERIAL_SVC_DATA_LEN_MAX, | ||||||
|  |                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|  |                                 &serial_svc->tx_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Add RX characteristic
 | ||||||
|  |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid, | ||||||
|  |                                 SERIAL_SVC_DATA_LEN_MAX,                                   | ||||||
|  |                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||||
|  |                                 ATTR_PERMISSION_NONE, | ||||||
|  |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                                 10, | ||||||
|  |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|  |                                 &serial_svc->rx_char_handle); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void serial_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(serial_svc) { | ||||||
|  |         // Delete characteristics
 | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(serial_svc); | ||||||
|  |         serial_svc = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { | ||||||
|  |     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); | ||||||
|  | 
 | ||||||
|  |     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||||
|  |                                           serial_svc->rx_char_handle, | ||||||
|  |                                           0, | ||||||
|  |                                           data_len, | ||||||
|  |                                           data); | ||||||
|  |     if(result) { | ||||||
|  |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed updating RX characteristic: %d", result); | ||||||
|  |     } | ||||||
|  |     return result != BLE_STATUS_SUCCESS; | ||||||
|  | } | ||||||
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
	 Aleksandr Kutuzov
						Aleksandr Kutuzov