mirror of
https://github.com/pine64/blisp.git
synced 2024-12-22 14:30:28 +00:00
6424f9ddf8
Add sp_drain() where needed. Without it FreeBSD won't pass the handshake phase. Also decreate max buffer size. Tested with Pinecil v2 and it can write and check new firmware.
415 lines
12 KiB
C
415 lines
12 KiB
C
// SPDX-License-Identifier: MIT
|
|
#include <blisp.h>
|
|
#include <blisp_util.h>
|
|
#include <libserialport.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __linux__
|
|
#include <linux/serial.h>
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#define DEBUG
|
|
|
|
static void drain(struct sp_port* port) {
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
sp_drain(port);
|
|
#endif
|
|
}
|
|
|
|
int32_t blisp_device_init(struct blisp_device* device,
|
|
struct blisp_chip* chip) {
|
|
device->chip = chip;
|
|
device->is_usb = false;
|
|
return 0;
|
|
}
|
|
|
|
int32_t blisp_device_open(struct blisp_device* device, const char* port_name) {
|
|
int ret;
|
|
struct sp_port* serial_port = NULL;
|
|
|
|
if (port_name != NULL) {
|
|
ret = sp_get_port_by_name(port_name, &serial_port);
|
|
if (ret != SP_OK) {
|
|
blisp_dlog("Couldn't open device, err: %d", ret);
|
|
return BLISP_ERR_CANT_OPEN_DEVICE;
|
|
}
|
|
} else {
|
|
if (!device->chip->usb_isp_available) {
|
|
return BLISP_ERR_NO_AUTO_FIND_AVAILABLE;
|
|
}
|
|
struct sp_port** port_list;
|
|
ret = sp_list_ports(&port_list);
|
|
if (ret != SP_OK) {
|
|
blisp_dlog("Couldn't list ports, err: %d", ret);
|
|
return BLISP_ERR_UNKNOWN;
|
|
}
|
|
for (int i = 0; port_list[i] != NULL; i++) {
|
|
struct sp_port* port = port_list[i];
|
|
|
|
int vid, pid;
|
|
sp_get_port_usb_vid_pid(port, &vid, &pid);
|
|
if (vid == 0xFFFF && pid == 0xFFFF) {
|
|
ret = sp_get_port_by_name(sp_get_port_name(port), &serial_port);
|
|
if (ret != SP_OK) {
|
|
blisp_dlog("Couldn't open device, err: %d", ret);
|
|
return BLISP_ERR_CANT_OPEN_DEVICE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
sp_free_port_list(port_list);
|
|
if (serial_port == NULL) {
|
|
return BLISP_ERR_DEVICE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
ret = sp_open(serial_port, SP_MODE_READ_WRITE);
|
|
if (ret != SP_OK) {
|
|
blisp_dlog("SP open failed: %d", ret);
|
|
return BLISP_ERR_UNKNOWN; // TODO: Maybe this should be that it can't open
|
|
// device?
|
|
}
|
|
// TODO: Handle errors in following functions, although, none of them *should*
|
|
// fail
|
|
sp_set_bits(serial_port, 8);
|
|
sp_set_parity(serial_port, SP_PARITY_NONE);
|
|
sp_set_stopbits(serial_port, 1);
|
|
sp_set_flowcontrol(serial_port, SP_FLOWCONTROL_NONE);
|
|
|
|
int vid, pid;
|
|
sp_get_port_usb_vid_pid(serial_port, &vid, &pid);
|
|
device->is_usb = pid == 0xFFFF;
|
|
// if (device->is_usb) {
|
|
// device->current_baud_rate = 2000000;
|
|
// } else {
|
|
device->current_baud_rate = 460800;
|
|
// }
|
|
|
|
#if 0
|
|
int fd;
|
|
sp_get_port_handle(serial_port, &fd);
|
|
struct serial_struct serial;
|
|
ioctl(fd, TIOCGSERIAL, &serial);
|
|
// serial.flags &= ~(ASYNC_LOW_LATENCY);
|
|
serial.flags |= ASYNC_LOW_LATENCY;
|
|
ioctl(fd, TIOCSSERIAL, &serial);
|
|
#endif
|
|
ret = sp_set_baudrate(serial_port, device->current_baud_rate);
|
|
if (ret != SP_OK) {
|
|
blisp_dlog("Set baud rate failed: %d... Also hello macOS user :)", ret);
|
|
return BLISP_ERR_UNKNOWN;
|
|
}
|
|
device->serial_port = serial_port;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_send_command(struct blisp_device* device,
|
|
uint8_t command,
|
|
void* payload,
|
|
uint16_t payload_size,
|
|
bool add_checksum) {
|
|
int ret;
|
|
struct sp_port* serial_port = device->serial_port;
|
|
|
|
device->tx_buffer[0] = command;
|
|
device->tx_buffer[1] = 0;
|
|
device->tx_buffer[2] = payload_size & 0xFF;
|
|
device->tx_buffer[3] = (payload_size >> 8) & 0xFF;
|
|
if (add_checksum) {
|
|
uint32_t checksum = 0;
|
|
checksum += device->tx_buffer[2] + device->tx_buffer[3];
|
|
for (uint16_t i = 0; i < payload_size; i++) {
|
|
checksum += *(uint8_t*)((uint8_t*)payload + i);
|
|
}
|
|
device->tx_buffer[1] = checksum & 0xFF;
|
|
}
|
|
if (payload_size != 0) {
|
|
memcpy(&device->tx_buffer[4], payload, payload_size);
|
|
}
|
|
ret =
|
|
sp_blocking_write(serial_port, device->tx_buffer, 4 + payload_size, 1000);
|
|
if (ret != (4 + payload_size)) {
|
|
blisp_dlog("Received error or not written all data: %d", ret);
|
|
return BLISP_ERR_UNKNOWN;
|
|
}
|
|
drain(serial_port);
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_receive_response(struct blisp_device* device,
|
|
bool expect_payload) {
|
|
// TODO: Check checksum
|
|
int ret;
|
|
struct sp_port* serial_port = device->serial_port;
|
|
ret = sp_blocking_read(serial_port, &device->rx_buffer[0], 2, 1000);
|
|
if (ret < 2) {
|
|
blisp_dlog("Failed to receive response, ret: %d", ret);
|
|
return BLISP_ERR_UNKNOWN; // TODO: Terrible
|
|
} else if (device->rx_buffer[0] == 'O' && device->rx_buffer[1] == 'K') {
|
|
if (expect_payload) {
|
|
sp_blocking_read(serial_port, &device->rx_buffer[2], 2,
|
|
100); // TODO: Check if really we received the data.
|
|
uint16_t data_length =
|
|
(device->rx_buffer[3] << 8) | (device->rx_buffer[2]);
|
|
sp_blocking_read(serial_port, &device->rx_buffer[0], data_length, 100);
|
|
return data_length;
|
|
}
|
|
return 0;
|
|
} else if (device->rx_buffer[0] == 'P' && device->rx_buffer[1] == 'D') {
|
|
return BLISP_ERR_PENDING; // TODO: This might be rather positive return
|
|
// number?
|
|
} else if (device->rx_buffer[0] == 'F' && device->rx_buffer[1] == 'L') {
|
|
sp_blocking_read(serial_port, &device->rx_buffer[2], 2, 100);
|
|
device->error_code = (device->rx_buffer[3] << 8) | (device->rx_buffer[2]);
|
|
blisp_dlog("Chip returned error: %d", device->error_code);
|
|
return BLISP_ERR_CHIP_ERR;
|
|
}
|
|
blisp_dlog("Failed to receive any response (err: %d, %d - %d)", ret,
|
|
device->rx_buffer[0], device->rx_buffer[1]);
|
|
return BLISP_ERR_UNKNOWN;
|
|
}
|
|
|
|
int32_t blisp_device_handshake(struct blisp_device* device, bool in_ef_loader) {
|
|
int ret;
|
|
uint8_t handshake_buffer[600];
|
|
struct sp_port* serial_port = device->serial_port;
|
|
|
|
if (!in_ef_loader && !device->is_usb) {
|
|
sp_set_rts(serial_port, SP_RTS_ON);
|
|
sp_set_dtr(serial_port, SP_DTR_ON);
|
|
sleep_ms(50);
|
|
sp_set_dtr(serial_port, SP_DTR_OFF);
|
|
sleep_ms(100);
|
|
sp_set_rts(serial_port, SP_RTS_OFF);
|
|
sleep_ms(50); // Wait a bit so BootROM can init
|
|
}
|
|
|
|
uint32_t bytes_count = device->chip->handshake_byte_multiplier *
|
|
(float)device->current_baud_rate / 10.0f;
|
|
if (bytes_count > 600)
|
|
bytes_count = 600;
|
|
memset(handshake_buffer, 'U', bytes_count);
|
|
|
|
for (uint8_t i = 0; i < 5; i++) {
|
|
if (!in_ef_loader) {
|
|
if (device->is_usb) {
|
|
sp_blocking_write(serial_port, "BOUFFALOLAB5555RESET\0\0", 22, 100);
|
|
drain(serial_port);
|
|
}
|
|
}
|
|
ret = sp_blocking_write(serial_port, handshake_buffer, bytes_count, 500);
|
|
// not sure about Apple part, but FreeBSD needs it
|
|
drain(serial_port);
|
|
if (ret < 0) {
|
|
blisp_dlog("Handshake write failed, ret %d", ret);
|
|
return BLISP_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (!in_ef_loader && !device->is_usb) {
|
|
sp_drain(serial_port); // Wait for write to send all data
|
|
sp_flush(serial_port, SP_BUF_INPUT); // Flush garbage out of RX
|
|
}
|
|
|
|
ret = sp_blocking_read(serial_port, device->rx_buffer, 2, 50);
|
|
if (ret >= 2) {
|
|
if (device->rx_buffer[0] == 'O' && device->rx_buffer[1] == 'K') {
|
|
return BLISP_OK;
|
|
}
|
|
}
|
|
|
|
}
|
|
blisp_dlog("Received no response from chip.");
|
|
return BLISP_ERR_NO_RESPONSE;
|
|
}
|
|
|
|
int32_t blisp_device_get_boot_info(struct blisp_device* device,
|
|
struct blisp_boot_info* boot_info) {
|
|
int ret;
|
|
|
|
ret = blisp_send_command(device, 0x10, NULL, 0, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = blisp_receive_response(device, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
memcpy(boot_info->boot_rom_version, &device->rx_buffer[0],
|
|
4); // TODO: Endianess
|
|
if (device->chip->type == BLISP_CHIP_BL70X) {
|
|
memcpy(boot_info->chip_id, &device->rx_buffer[16], 8);
|
|
}
|
|
// TODO: BL60X
|
|
return BLISP_OK;
|
|
}
|
|
|
|
// TODO: Use struct instead of uint8_t*
|
|
int32_t blisp_device_load_boot_header(struct blisp_device* device,
|
|
uint8_t* boot_header) {
|
|
int ret;
|
|
ret = blisp_send_command(device, 0x11, boot_header, 176, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_load_segment_header(
|
|
struct blisp_device* device,
|
|
struct blisp_segment_header* segment_header) {
|
|
int ret;
|
|
ret = blisp_send_command(device, 0x17, segment_header, 16, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, true); // TODO: Handle response
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_load_segment_data(struct blisp_device* device,
|
|
uint8_t* segment_data,
|
|
uint32_t segment_data_length) {
|
|
int ret;
|
|
ret = blisp_send_command(device, 0x18, segment_data, segment_data_length,
|
|
false);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_check_image(struct blisp_device* device) {
|
|
int ret;
|
|
ret = blisp_send_command(device, 0x19, NULL, 0, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_write_memory(struct blisp_device* device,
|
|
uint32_t address,
|
|
uint32_t value,
|
|
bool wait_for_res) {
|
|
int ret;
|
|
uint8_t payload[8];
|
|
*(uint32_t*)(payload) = address;
|
|
*(uint32_t*)(payload + 4) = value; // TODO: Endianness
|
|
ret = blisp_send_command(device, 0x50, payload, 8, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (wait_for_res) {
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_run_image(struct blisp_device* device) {
|
|
int ret;
|
|
|
|
if (device->chip->type == BLISP_CHIP_BL70X) { // ERRATA
|
|
ret = blisp_device_write_memory(device, 0x4000F100, 0x4E424845, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_device_write_memory(device, 0x4000F104, 0x22010000, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
// ret = blisp_device_write_memory(device, 0x40000018, 0x00000000);
|
|
// if (ret < 0) return ret;
|
|
ret = blisp_device_write_memory(device, 0x40000018, 0x00000002, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
return BLISP_OK;
|
|
}
|
|
|
|
ret = blisp_send_command(device, 0x1A, NULL, 0, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
int32_t blisp_device_flash_erase(struct blisp_device* device,
|
|
uint32_t start_address,
|
|
uint32_t end_address) {
|
|
uint8_t payload[8];
|
|
*(uint32_t*)(payload + 0) = start_address;
|
|
*(uint32_t*)(payload + 4) = end_address;
|
|
|
|
int ret = blisp_send_command(device, 0x30, payload, 8, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
do {
|
|
ret = blisp_receive_response(device, false);
|
|
} while (ret == BLISP_ERR_PENDING);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t blisp_device_flash_write(struct blisp_device* device,
|
|
uint32_t start_address,
|
|
uint8_t* payload,
|
|
uint32_t payload_size) {
|
|
// TODO: Add max payload size (8184?)
|
|
// TODO: Don't use malloc + add check
|
|
|
|
uint8_t* buffer = malloc(4 + payload_size);
|
|
*((uint32_t*)(buffer)) = start_address;
|
|
memcpy(buffer + 4, payload, payload_size);
|
|
int ret = blisp_send_command(device, 0x31, buffer, payload_size + 4, true);
|
|
if (ret < 0)
|
|
goto exit1;
|
|
ret = blisp_receive_response(device, false);
|
|
exit1:
|
|
free(buffer);
|
|
return ret;
|
|
}
|
|
|
|
int32_t blisp_device_program_check(struct blisp_device* device) {
|
|
int ret = blisp_send_command(device, 0x3A, NULL, 0, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t blisp_device_reset(struct blisp_device* device) {
|
|
int ret = blisp_send_command(device, 0x21, NULL, 0, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = blisp_receive_response(device, false);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return BLISP_OK;
|
|
}
|
|
|
|
void blisp_device_close(struct blisp_device* device) {
|
|
struct sp_port* serial_port = device->serial_port;
|
|
sp_close(serial_port);
|
|
} |