Files
orangepi-5-plus-kernel/external/cache/sources/hcitools/hciattach_xr.c

558 lines
15 KiB
C
Raw Normal View History

2026-02-07 20:22:48 +08:00
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <stdint.h>
#include <sys/termios.h>
#include <sys/ioctl.h>
#include <limits.h>
#include "hciattach.h"
/******************************************************************************
** Constants & Macros
******************************************************************************/
#define LOG_STR "XRADIO Bluetooth"
#define DBG_ON 1
#define XR_DBG(fmt, arg...) \
do { \
if (DBG_ON) \
fprintf(stderr, "%s: " fmt "\n" , LOG_STR, ##arg); \
} while(0)
#define XR_ERR(fmt, arg...) \
do { \
fprintf(stderr, "%s ERROR: " fmt "\n", LOG_STR, ##arg);\
perror(LOG_STR" ERROR reason"); \
} while(0)
#define XR_DUMP(buffer, len) \
fprintf(stderr, "%s: ", LOG_STR); \
do { \
for (int i = 0; i < len; i++) { \
if (i && !(i % 16)) { \
fprintf(stderr, "\n"); \
fprintf(stderr, "%s: ", LOG_STR); \
} \
fprintf(stderr, "%02x ", buffer[i]); \
} \
fprintf(stderr, "\n"); \
} while (0)
#define BT_FW_PATH_NAME "/system/vendor/etc/firmware/fw_xr829_bt.bin"
#define BT_FW_LOAD_ADDR 0x0000
#define BT_FW_JUMP_ADDR 0x0000
#define AW1722 1
#define AW1732 2
#define CHIP_NAME AW1722
#define SZ_1K (0x00000400U )
#define SZ_16K (0x00004000U )
#define SWAP16(d) (((d & 0xff) << 8) | ((d & 0xff00) >> 8))
#define SWAP32(d) (((d & 0xff) << 24) | ((d & 0xff00) << 8) \
| ((d & 0xff0000) >> 8) | ((d & 0xff000000) >> 24))
#define CMD_ID(group, key) (((group) << 3) | (key))
/*----------------------------*/
/* COMMANDS FORM PC TO MCU */
/*----------------------------*/
#define CMD_ID_MEMRW 0x00
#define CMD_ID_SEQRQ 0x01
#define CMD_ID_SYSCTL 0x02
#define CMD_ID_FLASH 0x03
#define CMD_ID_SEQRD CMD_ID(CMD_ID_SEQRQ, 0)
#define CMD_ID_SEQWR CMD_ID(CMD_ID_SEQRQ, 1)
/* uart commands */
#define CMD_ID_SETUART CMD_ID(CMD_ID_SYSCTL, 0)
#define CMD_ID_SETPC CMD_ID(CMD_ID_SYSCTL, 3)
#define CMD_WRITEN 0
#define CMD_WRITESEQ 1
#define CMD_SETBAUD 2
#define CMD_SETPC 3
#define DATA_RAW 4
/******************************************************************************
** Type definitions
******************************************************************************/
/* vendor serial control block */
typedef struct
{
int fd; /* fd to Bluetooth device */
struct termios *ti; /* serial terminal of BT port */
} vnd_userial_cb_t;
#pragma pack(1)
/* command header
*
* byte 0 byte 1 byte 2 byte 3 byte 4 byte 5 byte 6 -7 byte 8-11
* ___________________________________________________________________________________________________
* | | | | | | | | |
* | 'B' | 'R' | 'O' | 'M' | Flags |Reserved | Checksum | Playload Length |
* |_________|_________|_________|_________|_________|_________|__________ ________|___________________|
*/
typedef struct {
uint8_t magic[4]; // magic "BROM"
#define CMD_BROM_MAGIC "BROM"
uint8_t flags;
#define CMD_HFLAG_ERROR (0x1U << 0)
#define CMD_HFLAG_ACK (0x1U << 1)
#define CMD_HFLAG_CHECK (0x1U << 2)
#define CMD_HFLAG_RETRY (0x1U << 3)
#define CMD_HFLAG_EXE (0x1U << 4)
uint8_t version:4;
uint8_t reserved:4;
uint16_t checksum;
uint32_t payload_len;
} __attribute__((packed)) cmd_header_t;
#define MB_CMD_HEADER_SIZE (sizeof(cmd_header_t))
/* acknownledge structure */
typedef struct {
cmd_header_t h;
uint8_t err;
} __attribute__((packed)) cmd_ack_t;
/* sequence read/write command structure */
typedef struct {
cmd_header_t h;
uint8_t cmdid;
uint32_t addr;
uint32_t dlen;
uint16_t dcs;
} __attribute__((packed)) cmd_seq_wr_t;
/* io change command structure */
typedef struct {
cmd_header_t h;
uint8_t cmdid;
uint32_t val;
} __attribute__((packed)) cmd_sys_t;
typedef struct {
cmd_header_t h;
uint8_t cmdid;
uint32_t lcr;
} __attribute__((packed)) cmd_sys_setuart_t;
#pragma pack()
static const uint8_t hci_reset[] = { 0x01, 0x03, 0x0c, 0x00 };
static const uint8_t hci_update_baud_rate[] = { 0x01, 0x18, 0xfc, 0x04, 0x60, 0xE3, 0x16, 0x00};
static vnd_userial_cb_t vnd_userial;
static int32_t cmd_sync_uart(void);
static int32_t cmd_sync_baud(uint32_t lcr);
static int32_t cmd_write_seq(uint32_t addr, uint32_t len, uint8_t *data);
static int32_t cmd_set_pc(uint32_t pc);
static void userial_set_hw_fctrl(uint8_t hw_fctrl);
static uint32_t userial_read(uint8_t *p_buffer, uint32_t len, uint32_t timeout);
static uint32_t userial_write(const uint8_t *p_data, uint32_t len);
/******************************************************************************
** Functions
******************************************************************************/
static uint16_t CheckSum16(uint8_t *data, uint32_t len)
{
uint16_t cs = 0;
uint16_t *p = (uint16_t *)data;
while(len > 1) {
cs += *p++;
len -= 2;
}
if (len) {
cs += *(uint8_t *)p;
}
return cs;
}
static uint64_t time_gettimeofday_us(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
}
static uint8_t *memsearch(uint8_t *haystack, uint32_t hlen, uint8_t *needle, uint32_t nlen)
{
while (hlen-- >= nlen) {
if (!memcmp(haystack, needle, nlen)) {
return haystack;
}
haystack++;
}
return NULL;
}
static int32_t xr_raw_write(int type, uint8_t *data, uint32_t len)
{
uint8_t buffer[MB_CMD_HEADER_SIZE + 13] = {'B', 'R', 'O', 'M', 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t *psend = data;
uint32_t lsend = len;
cmd_header_t *hdr = (cmd_header_t *)buffer;;
cmd_ack_t *ack = (cmd_ack_t *)buffer;
if (type != DATA_RAW) {
psend = buffer;
lsend = MB_CMD_HEADER_SIZE + len;
memcpy(buffer + MB_CMD_HEADER_SIZE, data, len);
hdr->payload_len = len;
#if ENABLE_DCS
hdr->flags = CMD_HFLAG_CHECK;
#endif
hdr->checksum = ~CheckSum16(buffer, MB_CMD_HEADER_SIZE + len);
hdr->payload_len = SWAP32(hdr->payload_len);
hdr->checksum = SWAP16(hdr->checksum);
switch (type) {
case CMD_WRITESEQ:
{
cmd_seq_wr_t *cmd = (cmd_seq_wr_t *)buffer;
cmd->addr = SWAP32(cmd->addr);
cmd->dlen = SWAP32(cmd->dlen);
cmd->dcs = SWAP16(cmd->dcs);
}
break;
case CMD_SETBAUD:
{
cmd_sys_setuart_t *cmd = (cmd_sys_setuart_t*)buffer;
cmd->lcr = SWAP32(cmd->lcr);
}
break;
case CMD_SETPC:
{
cmd_sys_t *cmd = (cmd_sys_t*)buffer;
cmd->val = SWAP32(cmd->val);
}
break;
default:
XR_ERR("%s: Unsupport type %d", __func__, type);
return -1;
}
}
ssize_t ret_w, ret_r;
uint64_t t0, t1, t2, t3;
tcflush(vnd_userial.fd, TCIOFLUSH);
t0 = time_gettimeofday_us();
ret_w = userial_write(psend, lsend);
t1 = time_gettimeofday_us();
memset(buffer, 0, MB_CMD_HEADER_SIZE + 1);
t2 = time_gettimeofday_us();
ret_r = userial_read(buffer, MB_CMD_HEADER_SIZE, 100000);
t3 = time_gettimeofday_us();
XR_DBG("%s, type: %d, write len: %5d, ret: %5d, time: %6lluus, read len: %2d, ret: %2d, %6lluus",
__func__, type, lsend, ret_w, t1 - t0, MB_CMD_HEADER_SIZE, ret_r, t3 - t2);
uint8_t *p = (uint8_t *)memsearch(buffer, MB_CMD_HEADER_SIZE, (uint8_t *)"BROM", 4);
if (p != buffer) {
if (p == NULL) {
XR_ERR("%s: invalid response", __func__);
return -1;
}
uint32_t nowread = buffer + MB_CMD_HEADER_SIZE - p;
uint32_t needread = p - buffer;
XR_DBG("%s: Index error, re-find header magic", __func__);
memcpy(buffer, p, nowread);
memset(buffer + nowread, 0x0, needread);
userial_read(buffer + nowread, needread, 100000);
}
/* check response */
if (ack->h.flags & CMD_HFLAG_ERROR) {
userial_read(buffer + MB_CMD_HEADER_SIZE, 1, 100000);
XR_ERR("%s: resp error flag, type %d", __func__, ack->err);
return -ack->err;
}
if (ack->h.flags & CMD_HFLAG_ACK) {
/* convert network byte order to host byte order */
ack->h.payload_len = SWAP32(ack->h.payload_len);
ack->h.checksum = SWAP16(ack->h.checksum);
if (ack->h.payload_len != 0) {
XR_ERR("%s: data payload len %d != 0", __func__, ack->h.payload_len);
return -1;
}
}
if (ack->h.flags & CMD_HFLAG_CHECK) {
if (CheckSum16(buffer, MB_CMD_HEADER_SIZE) != 0xffff) {
XR_ERR("%s: write data response 0 checksum error", __func__);
return -1;
}
}
return 0;
}
static int32_t cmd_sync_uart(void)
{
uint8_t sync = 0x55;
uint8_t ack[3] = {0};
ssize_t ret = -1;
uint32_t cnt = 0;
do {
XR_DBG("uart sync count:%d.", cnt);
tcflush(vnd_userial.fd, TCIOFLUSH);
userial_write(&sync, 1);
ret = userial_read(ack, 2, 2000);
if (ret == 2 && ((ack[0] == 'O' && ack[1] == 'K') || (ack[0] == 'K' && ack[1] == 'O'))) {
XR_DBG("Receive %s, uart Sync done.", ack);
return 0;
}
} while (cnt++ < 50);
XR_DBG("uart sync fail.");
return -1;
}
static int32_t cmd_sync_baud(uint32_t lcr)
{
uint8_t buffer[MB_CMD_HEADER_SIZE + 5];
cmd_sys_setuart_t *cmd = (cmd_sys_setuart_t*)buffer;
cmd->cmdid = CMD_ID_SETUART;
cmd->lcr = lcr;
uint8_t cnt = 0;
int ret = -1;
do {
XR_DBG("%s count:%d.", __func__, cnt);
ret = xr_raw_write(CMD_SETBAUD, buffer + MB_CMD_HEADER_SIZE, 5);
if (ret == 0) {
set_speed(vnd_userial.fd, vnd_userial.ti, lcr & 0xffffff);
return cmd_sync_uart();
}
} while (cnt++ < 3);
XR_DBG("cmd_sync_baud fail.");
return -1;
}
static int32_t cmd_write_seq(uint32_t addr, uint32_t len, uint8_t *data)
{
int ret = -1;
uint8_t buffer[MB_CMD_HEADER_SIZE + 13];
cmd_seq_wr_t *cmd = (cmd_seq_wr_t *)buffer;
cmd->cmdid = CMD_ID_SEQWR;
cmd->addr = addr;
cmd->dlen = len;
#if ENABLE_DCS
cmd->dcs = ~CheckSum16(data, len);
#endif
ret = xr_raw_write(CMD_WRITESEQ, buffer + MB_CMD_HEADER_SIZE, 11);
if (ret == 0) {
return xr_raw_write(DATA_RAW, data, len);
}
return ret;
}
static int32_t cmd_set_pc(uint32_t pc)
{
uint8_t buffer[MB_CMD_HEADER_SIZE + 5];
cmd_sys_t *cmd = (cmd_sys_t*)buffer;
cmd->cmdid = CMD_ID_SETPC;
cmd->val = pc;
XR_DBG("set pc %x, val %x", pc, cmd->val);
return xr_raw_write(CMD_SETPC, buffer + MB_CMD_HEADER_SIZE, 5);
}
static void userial_set_hw_fctrl(uint8_t hw_fctrl)
{
if (vnd_userial.fd == -1) {
XR_ERR("vnd_userial.fd is -1");
return;
}
if (hw_fctrl) {
XR_DBG("Set HW FlowControl On");
vnd_userial.ti->c_cflag |= CRTSCTS;
} else {
XR_DBG("Set HW FlowControl Off");
vnd_userial.ti->c_cflag &= ~CRTSCTS;
}
tcsetattr(vnd_userial.fd, TCSANOW, vnd_userial.ti);
tcflush(vnd_userial.fd, TCIOFLUSH);
}
static uint32_t userial_read(uint8_t *buffer, uint32_t len, uint32_t timeout)
{
fd_set set;
struct timeval tv;
int rv;
FD_ZERO(&set); /* clear the set */
FD_SET(vnd_userial.fd, &set); /* add our file descriptor to the set */
/* there was data to read */
ssize_t r;
uint8_t *pos = (uint8_t*)buffer;
while (len > 0) {
tv.tv_sec = 0;
tv.tv_usec = timeout;
rv = select(vnd_userial.fd + 1, &set, NULL, NULL, &tv);
if(rv == -1) {
XR_ERR("select error"); /* an error accured */
break;
} else if(rv == 0) {
XR_ERR("read timeout"); /* a timeout occured */
break;
}
r = read(vnd_userial.fd, pos, len);
if (r < 1)
break;
len -= r;
pos += r;
}
return pos - buffer;
}
static uint32_t userial_write(const uint8_t *buffer, uint32_t len)
{
ssize_t r;
uint8_t *pos = (uint8_t*)buffer;
while (len > 0) {
r = write(vnd_userial.fd, pos, len);
if (r < 1)
break;
len -= r;
pos += r;
}
return pos - buffer;
}
static int32_t load_btfirmware(void)
{
FILE *fwfile_fd = NULL;
uint32_t len;
uint8_t *data = NULL;
uint32_t addr = BT_FW_LOAD_ADDR;
uint32_t section = SZ_16K;
fwfile_fd = fopen(BT_FW_PATH_NAME, "rb");
XR_DBG("BT firmware: %s", BT_FW_PATH_NAME);
if(!fwfile_fd) {
XR_ERR("Unable to open BT firmware %s", BT_FW_PATH_NAME);
return -1;
}
data = (uint8_t*)malloc(section);
if (data == NULL) {
XR_DBG("failed to alloc %d byte memory.", section);
fclose(fwfile_fd);
return -1;
}
XR_DBG("load bt firmware starting.");
while ((len = fread(data, 1, section, fwfile_fd)) > 0) {
cmd_write_seq(addr, len, data);
addr += len;
}
free(data);
fclose(fwfile_fd);
XR_DBG("load firmware done.");
XR_DBG("Firmware run from address 0x%08X", BT_FW_JUMP_ADDR);
cmd_set_pc(BT_FW_JUMP_ADDR);
if (CHIP_NAME == AW1732) {
XR_DBG("second time sync starting....");
if (cmd_sync_uart() < 0)
return -1;
cmd_set_pc(BT_FW_JUMP_ADDR);
}
return addr;
}
static int hci_cmd_handle(const uint8_t *cmd, uint32_t cmd_len, uint32_t event_len)
{
uint8_t buffer[256];
XR_DBG("send hci command");
userial_write(cmd, cmd_len);
XR_DUMP(cmd, cmd_len);
if (read_hci_event(vnd_userial.fd, buffer, event_len) != event_len) {
XR_ERR("Event read error");
return -1;
}
XR_DBG("Received event");
XR_DUMP(buffer, event_len);
return 0;
}
int xr_init(int fd, struct uart_t *u, struct termios *ti)
{
vnd_userial.fd = fd;
vnd_userial.ti = ti;
XR_DBG("uart sync starting....");
if (cmd_sync_uart() < 0)
goto END;
XR_DBG("set bandrate to %d.", u->speed);
if (cmd_sync_baud(((u->speed) | (3<<24))) < 0)
goto END;
if (load_btfirmware() < 0)
goto END;
XR_DBG("bt firmware is running....");
XR_DBG("set baudrate to %d", u->init_speed);
set_speed(vnd_userial.fd, vnd_userial.ti, u->init_speed);
userial_set_hw_fctrl(1);
usleep(50000);
XR_DBG("process hci reset...");
if (hci_cmd_handle(hci_reset, sizeof(hci_reset), 7) < 0)
goto END;
XR_DBG("process hci update baud...");
if (hci_cmd_handle(hci_update_baud_rate, sizeof(hci_update_baud_rate), 7) < 0)
goto END;
usleep(100000);
return 0;
END:
XR_DBG("device fd = %d close", fd);
close(vnd_userial.fd);
vnd_userial.fd = -1;
vnd_userial.ti = NULL;
return -1;
}
int xr_post(int fd, struct uart_t *u, struct termios *ti)
{
XR_DBG("Done setting line discpline");
return 0;
}