Added source code

Ver 0.5
This commit is contained in:
prifak
2025-06-10 10:09:49 +02:00
committed by GitHub
parent b9e091fc92
commit 0cf9769518
4 changed files with 332 additions and 0 deletions

31
Makefile Normal file
View File

@@ -0,0 +1,31 @@
CC = gcc
CFLAGS = -Wall -Wextra -O2 `pkg-config --cflags hidapi-hidraw`
LDFLAGS = `pkg-config --libs hidapi-hidraw`
SRC_DIR = src
BUILD_DIR = .build
BIN_DIR = bin
OUT = $(BIN_DIR)/annepro2_flasher_c
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS))
PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin
all: $(BIN_DIR) $(BUILD_DIR) $(OUT)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
$(OUT): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
$(BIN_DIR) $(BUILD_DIR):
mkdir -p $@
install: $(OUT)
install -Dm755 $(OUT) $(BINDIR)/annepro2_tools_c
clean:
rm -rf $(BUILD_DIR) $(BIN_DIR)

192
src/annepro2.c Normal file
View File

@@ -0,0 +1,192 @@
#include "annepro2.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#define ANNEPRO2_VID 0x04d9
#define PID_C15 0x8008
#define PID_C18 0x8009
static int write_to_target(hid_device *handle, AP2Target target, const uint8_t *payload, size_t payload_len);
static const char *flash_error_msgs[] = {
"No device found",
"Multiple devices found",
"USB error",
"Erase error",
"Flash error",
"Unknown error"
};
const char* get_flash_error_msg(AP2FlashError err) {
if (err < 0 || err >= OtherError) return flash_error_msgs[OtherError];
return flash_error_msgs[err];
}
int flash_firmware(AP2Target target, uint32_t base, FILE *file, int boot) {
hid_device *handle = NULL;
struct hid_device_info *devices = NULL, *dev = NULL;
// Первый поиск
devices = hid_enumerate(ANNEPRO2_VID, 0);
for (dev = devices; dev; dev = dev->next) {
if ((dev->product_id == PID_C15 && dev->interface_number == 1) ||
dev->product_id == PID_C18) {
handle = hid_open_path(dev->path);
break;
}
}
hid_free_enumeration(devices);
// Ожидание устройства
if (!handle) {
printf("Please put your keyboard into IAP mode (hold ESC while plugging it in).\n");
for (int i = 10; i > 0; i--) {
sleep(1);
devices = hid_enumerate(ANNEPRO2_VID, 0);
for (dev = devices; dev; dev = dev->next) {
if ((dev->product_id == PID_C15 && dev->interface_number == 1) ||
dev->product_id == PID_C18) {
handle = hid_open_path(dev->path);
break;
}
}
hid_free_enumeration(devices);
if (handle) break;
printf("Waiting... %ds remaining\r", i);
fflush(stdout);
}
printf("\n");
}
if (!handle) {
fprintf(stderr, "No compatible device found after waiting. Ensure the keyboard is in IAP mode.\n");
return NoDeviceFound;
}
// Стирание
uint8_t erase_cmd[6] = {
0x02, 0x43,
base & 0xFF, (base >> 8) & 0xFF,
(base >> 16) & 0xFF, (base >> 24) & 0xFF
};
if (write_to_target(handle, target, erase_cmd, sizeof(erase_cmd)) < 0) {
fprintf(stderr, "Error erasing device memory.\n");
hid_close(handle);
return EraseError;
}
// Получение размера прошивки
fseek(file, 0, SEEK_END);
size_t total_size = ftell(file);
rewind(file);
uint8_t chunk[48];
size_t read_size;
uint32_t current_addr = base;
size_t total_written = 0;
time_t start = time(NULL);
while ((read_size = fread(chunk, 1, sizeof(chunk), file)) > 0) {
uint8_t flash_cmd[6 + 48] = {
0x02, 0x31,
current_addr & 0xFF, (current_addr >> 8) & 0xFF,
(current_addr >> 16) & 0xFF, (current_addr >> 24) & 0xFF
};
memcpy(flash_cmd + 6, chunk, read_size);
if (write_to_target(handle, target, flash_cmd, 6 + read_size) < 0) {
fprintf(stderr, "\nError flashing memory at address 0x%08x.\n", current_addr);
hid_close(handle);
return FlashError;
}
total_written += read_size;
current_addr += read_size;
float percent = (float)total_written / total_size;
int bars = (int)(percent * 30);
time_t now = time(NULL);
int elapsed = (int)difftime(now, start);
printf("\r[");
for (int i = 0; i < 30; i++) {
if (i < bars) printf("=");
else printf(" ");
}
printf("] %3.0f%% | %zu/%zu bytes | %ds elapsed", percent * 100, total_written, total_size, elapsed);
fflush(stdout);
}
printf("\nFlash complete: %zu bytes written in %lds.\n", total_written, time(NULL) - start);
// Установка AP-флага
uint8_t ap_flag_cmd[3] = {0x02, 0x32, 0x02};
if (write_to_target(handle, McuMain, ap_flag_cmd, sizeof(ap_flag_cmd)) < 0) {
fprintf(stderr, "Error writing AP flag.\n");
hid_close(handle);
return FlashError;
}
// Рестарт устройства, если нужно
if (boot) {
/*
uint8_t boot_cmd[] = {
0x00, 0x7b, 0x10, 0x31,
0x10, 0x03, 0x00, 0x00,
0x7d, 0x02, 0x01, 0x02
};
if (hid_write(handle, boot_cmd, sizeof(boot_cmd)) < 0) {
fprintf(stderr, "Error booting device.\n");
hid_close(handle);
return FlashError;
}
*/
uint8_t boot_cmd[] = {
0x00, 0x7b, 0x10, 0x31,
0x10, 0x03, 0x00, 0x00,
0x7d, 0x02, 0x01, 0x02
};
if (hid_write(handle, boot_cmd, sizeof(boot_cmd)) < 0) {
fprintf(stderr, "Error booting device.\n");
hid_close(handle);
return FlashError;
}
}
hid_close(handle);
return 0;
}
static int write_to_target(hid_device *handle, AP2Target target, const uint8_t *payload, size_t payload_len) {
uint8_t buffer[64] = {0};
buffer[1] = 0x7b;
buffer[2] = 0x10;
buffer[3] = ((target & 0x0F) << 4) | UsbHost;
buffer[4] = 0x10;
buffer[5] = (uint8_t)payload_len;
buffer[6] = 0x00;
buffer[7] = 0x00;
buffer[8] = 0x7d;
memcpy(buffer + 9, payload, payload_len);
// Padding
memset(buffer + 9 + payload_len, 0, 64 - 9 - payload_len);
if (hid_write(handle, buffer, sizeof(buffer)) < 0) {
fprintf(stderr, "HID write error.\n");
return -1;
}
uint8_t response[64] = {0};
if (hid_read(handle, response, sizeof(response)) < 0) {
fprintf(stderr, "HID read error.\n");
return -1;
}
return 0;
}

30
src/annepro2.h Normal file
View File

@@ -0,0 +1,30 @@
// annepro2.h
#ifndef ANNEPRO2_H
#define ANNEPRO2_H
#include <stdint.h>
#include <stdio.h>
#include "hidapi.h"
typedef enum {
UsbHost = 1,
BleHost = 2,
McuMain = 3,
McuLed = 4,
McuBle = 5
} AP2Target;
typedef enum {
NoDeviceFound,
MultipleDeviceFound,
USBError,
EraseError,
FlashError,
OtherError
} AP2FlashError;
int flash_firmware(AP2Target target, uint32_t base, FILE *file, int boot);
const char* get_flash_error_msg(AP2FlashError err);
#endif

79
src/main.c Normal file
View File

@@ -0,0 +1,79 @@
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "annepro2.h"
uint32_t parse_hex(const char *str) {
if (strncmp(str, "0x", 2) == 0 || strncmp(str, "0X", 2) == 0) {
return (uint32_t)strtoul(str + 2, NULL, 16);
}
return (uint32_t)strtoul(str, NULL, 16);
}
void print_usage(const char *prog) {
printf("Usage: %s [--base <hex>] [--boot] [-t <target>] <file>\n", prog);
printf("Targets: main, led, ble\n");
}
int main(int argc, char *argv[]) {
uint32_t base = 0x4000;
int boot = 0;
const char *target_str = "main";
const char *file_path = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--base") == 0 && i + 1 < argc) {
base = parse_hex(argv[++i]);
} else if (strcmp(argv[i], "--boot") == 0) {
boot = 1;
} else if ((strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--target") == 0) && i + 1 < argc) {
target_str = argv[++i];
} else if (argv[i][0] != '-') {
file_path = argv[i];
} else {
print_usage(argv[0]);
return 1;
}
}
if (!file_path) {
print_usage(argv[0]);
return 1;
}
AP2Target target;
if (strcasecmp(target_str, "main") == 0) {
target = McuMain;
} else if (strcasecmp(target_str, "led") == 0) {
target = McuLed;
} else if (strcasecmp(target_str, "ble") == 0) {
target = McuBle;
} else {
fprintf(stderr, "Invalid target: %s\n", target_str);
return 1;
}
FILE *f = fopen(file_path, "rb");
if (!f) {
perror("Failed to open file");
return 1;
}
int result = flash_firmware(target, base, f, boot);
fclose(f);
if (result == 0) {
printf("Flash complete\n");
if (boot) {
printf("Booting Keyboard\n");
}
} else {
fprintf(stderr, "Flash error: %s\n", get_flash_error_msg(result));
return 1;
}
return 0;
}