mirror of
https://github.com/QIDITECH/klipper.git
synced 2026-01-31 07:58:42 +03:00
klipper update
This commit is contained in:
246
lib/rp2040_flash/main.c
Normal file
246
lib/rp2040_flash/main.c
Normal file
@@ -0,0 +1,246 @@
|
||||
// Simple rp2040 picoboot based flash tool for use with Klipper
|
||||
//
|
||||
// Copyright (C) 2022 Lasse Dalegaard <dalegaard@gmail.com>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "picoboot_connection.h"
|
||||
#include "boot/uf2.h"
|
||||
|
||||
#define FLASH_MAX_SIZE (FLASH_END - FLASH_START)
|
||||
#define FLASH_NUM_WRITE_BLOCKS (FLASH_MAX_SIZE / PAGE_SIZE)
|
||||
#define FLASH_NUM_ERASE_BLOCKS (FLASH_MAX_SIZE / FLASH_SECTOR_ERASE_SIZE)
|
||||
|
||||
struct flash_data {
|
||||
size_t num_blocks;
|
||||
uint8_t flash_data[FLASH_MAX_SIZE];
|
||||
bool write_blocks[FLASH_NUM_WRITE_BLOCKS];
|
||||
bool erase_blocks[FLASH_NUM_ERASE_BLOCKS];
|
||||
};
|
||||
|
||||
int load_flash_data(const char *filename, struct flash_data *target) {
|
||||
int rc = 0;
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open image file %s\n", filename);
|
||||
rc = errno;
|
||||
goto do_exit;
|
||||
}
|
||||
|
||||
target->num_blocks = 0;
|
||||
memset(target->write_blocks, 0, sizeof(target->write_blocks));
|
||||
memset(target->erase_blocks, 0, sizeof(target->erase_blocks));
|
||||
|
||||
struct uf2_block block;
|
||||
|
||||
while (1) {
|
||||
if(fread(&block, sizeof(struct uf2_block), 1, file) != 1) {
|
||||
if (feof(file)) {
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "Unexpected EOF reading image\n");
|
||||
rc = errno;
|
||||
goto do_exit;
|
||||
}
|
||||
|
||||
// Check magic numbers
|
||||
if (block.magic_start0 != UF2_MAGIC_START0) continue;
|
||||
if (block.magic_start1 != UF2_MAGIC_START1) continue;
|
||||
if (block.magic_end != UF2_MAGIC_END) continue;
|
||||
|
||||
// Check block is valid for flashing
|
||||
// Always family ID.
|
||||
if (!(block.flags & UF2_FLAG_FAMILY_ID_PRESENT)) continue;
|
||||
if (block.file_size != RP2040_FAMILY_ID) continue;
|
||||
if (block.flags & UF2_FLAG_NOT_MAIN_FLASH) continue;
|
||||
if (block.payload_size != PAGE_SIZE) continue;
|
||||
|
||||
// Bounds and alignment checking
|
||||
if (block.target_addr != (block.target_addr & ~(PAGE_SIZE-1))) continue;
|
||||
if (block.target_addr > FLASH_END - PAGE_SIZE) continue;
|
||||
if (block.target_addr < FLASH_START) continue;
|
||||
|
||||
uint32_t offset = block.target_addr - FLASH_START;
|
||||
|
||||
// Copy data and mark the matching write and erase blocks
|
||||
memcpy(&target->flash_data[offset], block.data, PAGE_SIZE);
|
||||
target->write_blocks[offset / PAGE_SIZE] = 1;
|
||||
target->erase_blocks[offset / FLASH_SECTOR_ERASE_SIZE] = 1;
|
||||
|
||||
target->num_blocks++;
|
||||
}
|
||||
|
||||
do_exit:
|
||||
if (file) {
|
||||
fclose(file);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *status_codes_strings[] = {
|
||||
"ok",
|
||||
"unknown command",
|
||||
"bad address alignment",
|
||||
"interleaved write",
|
||||
"invalid address",
|
||||
"invalid cmd length",
|
||||
"invalid transfer length",
|
||||
"rebooting",
|
||||
"unknown error",
|
||||
};
|
||||
|
||||
int report_error(libusb_device_handle *handle, const char *cmd) {
|
||||
struct picoboot_cmd_status status;
|
||||
status.dStatusCode = 0;
|
||||
int rc = picoboot_cmd_status(handle, &status);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Command %s failed, and it was not possible to "
|
||||
"query PICOBOOT status\n", cmd);
|
||||
} else {
|
||||
if (status.dStatusCode == 0) status.dStatusCode = 8;
|
||||
fprintf(stderr, "Command %s failed with status %d: %s\n",
|
||||
cmd, status.dStatusCode,
|
||||
status_codes_strings[status.dStatusCode]);
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
int picoboot_flash(libusb_device_handle *handle, struct flash_data *image) {
|
||||
fprintf(stderr, "Resetting interface\n");
|
||||
if (picoboot_reset(handle)) {
|
||||
return report_error(handle, "reset");
|
||||
}
|
||||
|
||||
fprintf(stderr, "Locking\n");
|
||||
if (picoboot_exclusive_access(handle, EXCLUSIVE)) {
|
||||
return report_error(handle, "exclusive_access");
|
||||
}
|
||||
|
||||
fprintf(stderr, "Exiting XIP mode\n");
|
||||
if (picoboot_exit_xip(handle)) {
|
||||
return report_error(handle, "exit_xip");
|
||||
}
|
||||
|
||||
fprintf(stderr, "Erasing\n");
|
||||
for(size_t i = 0; i < FLASH_NUM_ERASE_BLOCKS; i++) {
|
||||
if (!image->erase_blocks[i]) continue;
|
||||
uint32_t addr = FLASH_START + i * FLASH_SECTOR_ERASE_SIZE;
|
||||
if (picoboot_flash_erase(handle, addr, FLASH_SECTOR_ERASE_SIZE)) {
|
||||
return report_error(handle, "flash_erase");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Flashing\n");
|
||||
for(size_t i = 0; i < FLASH_NUM_WRITE_BLOCKS; i++) {
|
||||
if (!image->write_blocks[i]) continue;
|
||||
uint32_t addr = FLASH_START + i * PAGE_SIZE;
|
||||
uint8_t *buf = &image->flash_data[i * PAGE_SIZE];
|
||||
if (picoboot_write(handle, addr, buf, PAGE_SIZE)) {
|
||||
return report_error(handle, "write");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Rebooting device\n");
|
||||
if (picoboot_reboot(handle, 0, 0, 500)) {
|
||||
return report_error(handle, "reboot");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_usage(char *argv[]) {
|
||||
fprintf(stderr, "Usage: %s <uf2 image> [bus addr]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
libusb_context *ctx = 0;
|
||||
struct libusb_device **devs = 0;
|
||||
libusb_device_handle *handle = 0;
|
||||
struct flash_data *image = malloc(sizeof(struct flash_data));
|
||||
int rc = 0;
|
||||
|
||||
if (argc != 2 && argc != 4) {
|
||||
print_usage(argv);
|
||||
}
|
||||
|
||||
if (load_flash_data(argv[1], image)) {
|
||||
fprintf(stderr, "Could not load flash image, exiting\n");
|
||||
rc = 1;
|
||||
goto do_exit;
|
||||
}
|
||||
fprintf(stderr, "Loaded UF2 image with %lu pages\n", image->num_blocks);
|
||||
|
||||
bool has_target = false;
|
||||
uint8_t target_bus = 0;
|
||||
uint8_t target_address = 0;
|
||||
if(argc == 4) {
|
||||
has_target = true;
|
||||
|
||||
char *endptr;
|
||||
target_bus = strtol(argv[2], &endptr, 10);
|
||||
if (endptr == argv[2] || *endptr != 0) print_usage(argv);
|
||||
|
||||
target_address = strtol(argv[3], &endptr, 10);
|
||||
if (endptr == argv[3] || *endptr != 0) print_usage(argv);
|
||||
}
|
||||
|
||||
if (libusb_init(&ctx)) {
|
||||
fprintf(stderr, "Could not initialize libusb\n");
|
||||
rc = 1;
|
||||
goto do_exit;
|
||||
}
|
||||
|
||||
ssize_t cnt = libusb_get_device_list(ctx, &devs);
|
||||
if (cnt < 0) {
|
||||
fprintf(stderr, "Failed to enumerate USB devices: %s",
|
||||
libusb_strerror(cnt));
|
||||
rc = 1;
|
||||
goto do_exit;
|
||||
}
|
||||
|
||||
for (libusb_device **dev = devs; *dev; ++dev) {
|
||||
if (has_target) {
|
||||
if (target_bus != libusb_get_bus_number(*dev)) continue;
|
||||
if (target_address != libusb_get_device_address(*dev)) continue;
|
||||
}
|
||||
enum picoboot_device_result res = picoboot_open_device(*dev, &handle);
|
||||
if (res == dr_vidpid_bootrom_ok) {
|
||||
break;
|
||||
}
|
||||
if (handle) {
|
||||
libusb_close(handle);
|
||||
handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
fprintf(stderr, "No rp2040 in BOOTSEL mode was found\n");
|
||||
goto do_exit;
|
||||
}
|
||||
|
||||
libusb_device *dev = libusb_get_device(handle);
|
||||
fprintf(stderr, "Found rp2040 device on USB bus %d address %d\n",
|
||||
libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
||||
fprintf(stderr, "Flashing...\n");
|
||||
|
||||
rc = picoboot_flash(handle, image);
|
||||
|
||||
do_exit:
|
||||
if (handle) {
|
||||
libusb_close(handle);
|
||||
}
|
||||
if (devs) {
|
||||
libusb_free_device_list(devs, 1);
|
||||
}
|
||||
if (ctx) {
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
free(image);
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user