前面我们分享了openocd的构建过程,以及使用gdb进行仿真调试,为开发做好了准备。现在我们就来分享如何为openocd适配新的驱动,即适配新的仿真器硬件。
假设我们要添加一个我们自己的xxlink硬件驱动。
修改configure.ac
先添加适配器类型,在
m4_define([USB1_ADAPTERS],下添加一行
[[xxlink], [XXLINK devices], [XXLINK]],
在AC_ARG_ENABLE 部分,添加内容如下:
AC_ARG_ENABLE([xxlink],
AS_HELP_STRING([--enable-xxlink], [Enable building support for XXLINK]),
[build_xxlink=$enableval], [build_xxlink=no])
在AS_IF 部分,添加内容如下:
AS_IF([test "x$build_xxlink" = "xyes"], [
AC_DEFINE([BUILD_XXLINK], [1], [1 if you want XXLINK.])
], [
AC_DEFINE([BUILD_XXLINK], [0], [0 if you don't want XXLINK.])
])
在AM_CONDITIONAL 部分,添加内容如下:
AM_CONDITIONAL([XXLINK], [test "x$build_xxlink" = "xyes"])
参考ft232r.c
添加xxlink.c
即要实现
struct adapter_driver xxlink_adapter_driver = {
};
可以看到要实现一个新的驱动只要实现该结构体就可以了,
可以看到jtag操作就是要实现
xxlink_interface..execute_queue,这个我们后文再分享。
我们先实现简单的框架保证能先编译
src\jtag\drivers\Makefile.am中添加
if XXLINK
DRIVERFILES += %D%/xxlink.c
endif
src\jtag\interface.h中申明全局变量xxlink_adapter_driver
最后添加一行
extern struct adapter_driver xxlink_adapter_driver;
src\jtag\interfaces.c中数组adapter_drivers最后添加
#if BUILD_XXLINK == 1
&xxlink_adapter_driver,
#endif
Make后会自动在build\config.h中
生成宏
/* 0 if you don't want XXLINK. */
#define BUILD_XXLINK 1
添加--enable-xxlink参数使能编译对应的驱动
../configure --prefix=/home/qinyunti/openocd/build --enable-xxlink
看到会打印
XXLINK devices yes
make -j4
make install
tcl/target下
新建文件xxlink.cfg
adapter driver xxlink
adapter speed 1000
xxlink vid_pid 0x1122 0x3344
make install时会自动复制到build\share\openocd\scripts\interface
以上我们分享了如何添加一个驱动支持,现在驱动只是一个简单的框架还未实现任何功能,
我们先来了解openocd是如何找到我们实现的驱动,如何给驱动传递参数的。这有助于我们后面驱动的实现,比如我们要给驱动做一些参数配置等。
使用仿真跟踪方便查看执行过程
gdb --args bin/openocd.exe -f xxlink.cfg
我们一开始肯定想要知道openocd是如何找到我们的驱动的。
实际上openocd都是通过命令行中传入的参数-f指定的cfg文件来实现的。
cfg文件可以认为是一个脚本文件,openocd会解析该文件中的命令行,根据字符串进行匹配,调用对应的命令处理函数。
我们来看如何识别驱动的过程:
.\openocd.exe -f xxlink.cfg执行时
执行到main->openocd_main->openocd_thread->parse_cmdline_args->
char *command = alloc_printf("script {%s}", optarg); 之后
malloc了缓存command,且其内容初始化为了"script {xxlink.cfg}",可以指定多个cfg文件
继续
add_config_command(command);
config_file_names是 char **指针,
首先将其realloc扩展一个char* 指针位置,
然后strdup动态分配空间,将cfg字符串复制到该动态空间,
config_file_names[num_config_files-1]指向该动态空间。
num_config_files++;记录有多少个配置文件
void add_config_command(const char *cfg)
{
num_config_files++;
config_file_names = realloc(config_file_names, (num_config_files + 1) * sizeof(char *));
config_file_names[num_config_files-1] = strdup(cfg);
config_file_names[num_config_files] = NULL;
}
然后处理配置文件
openocd_thread->parse_config_file
如果上面没有指定配置文件即config_file_names为空,则使用默认配置文件
script openocd.cfg
如果指定了配置文件,则遍历配置文件config_file_names,
每个配置文件都调用command_run_line进行处理
int parse_config_file(struct command_context *cmd_ctx)
{
int retval;
char **cfg;
if (!config_file_names) {
command_run_line(cmd_ctx, "script openocd.cfg");
return ERROR_OK;
}
cfg = config_file_names;
while (*cfg) {
retval = command_run_line(cmd_ctx, *cfg);
if (retval != ERROR_OK)
return retval;
cfg++;
}
return ERROR_OK;
}
command_run_line中
调用retcode = Jim_Eval_Named(interp, line, NULL, 0);
即整个过程是根据xxlink.cfg中的
adapter driver xxlink
先根据adapter匹配interface_command_handlers的.name = "adapter"
继续搜寻子命令.chain = adapter_command_handlers,
根据driver匹配adapter_command_handlers的.name = "driver",
调用.handler = handle_adapter_driver_command,
在handle_adapter_driver_command中匹配xxlink和adapter_drivers这个数组成员的
name, 匹配之后设置全局变量记录使用的驱动adapter_driver = adapter_drivers[i];
如果我们要给驱动传递一些参数怎么做呢,依旧是通过cfg文件中的命令行来实现。
先写一个简单的驱动程序测试
类似adapter driver xxlink
xxlink.cfg中
xxlink vid_pid 0x1122 0x3344
会根据xxlink找到驱动
xxlink_adapter_driver
然后找到xxlink_command_handlers匹配
.name = "xxlink",
下一级还是子命令
.chain = xxlink_subcommand_handlers,
继续找到xxlink_subcommand_handlers匹配
.name = "vid_pid",
回调
.handler = xxlink_handle_vid_pid_command,
对应的代码如下
Xxlink.c
// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* Copyright (C) 2024 xx *
* xxxxx@xxx.xx *
***************************************************************************/
/* project specific includes */
/* system includes */
static uint16_t xxlink_vid = 0x0403;
static uint16_t xxlink_pid = 0x6001;
static int xxlink_init(void)
{
LOG_DEBUG("xxlink_init");
return ERROR_OK;
}
static int xxlink_quit(void)
{
LOG_DEBUG("xxlink_quit");
return ERROR_OK;
}
static int xxlink_speed_div(int divisor, int *khz)
{
/* Maximum 3 Mbaud. */
if (divisor == 0)
*khz = 3000;
else if (divisor == 1)
*khz = 2000;
else
*khz = 3000 / divisor;
LOG_DEBUG("xxlink_speed_div divisor=%d rate %d khz", divisor, *khz);
return ERROR_OK;
}
static int xxlink_khz(int khz, int *divisor)
{
if (khz == 0) {
LOG_DEBUG("RCLK not supported");
return ERROR_FAIL;
}
/* Calculate frequency divisor. */
if (khz > 2500)
*divisor = 0; /* Special case: 3 MHz */
else if (khz > 1700)
*divisor = 1; /* Special case: 2 MHz */
else {
*divisor = (2*3000 / khz + 1) / 2;
if (*divisor > 0x3FFF)
*divisor = 0x3FFF;
}
LOG_DEBUG("xxlink_khz %d divisor=%d", khz, *divisor);
return ERROR_OK;
}
static int xxlink_speed(int divisor)
{
int baud = (divisor == 0) ? 3000000 :
(divisor == 1) ? 2000000 :
3000000 / divisor;
LOG_DEBUG("xxlink_speed(%d) rate %d bits/sec", divisor, baud);
//if (jtag_libusb_control_transfer(adapter,
// LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
// SIO_SET_BAUD_RATE, divisor, 0, NULL, 0, 1000, NULL) != ERROR_OK) {
// LOG_ERROR("cannot set baud rate");
// return ERROR_JTAG_DEVICE_ERROR;
//}
return ERROR_OK;
}
COMMAND_HANDLER(xxlink_handle_vid_pid_command)
{
if (CMD_ARGC > 2) {
LOG_WARNING("ignoring extra IDs in ft232r_vid_pid "
"(maximum is 1 pair)");
CMD_ARGC = 2;
}
if (CMD_ARGC == 2) {
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], xxlink_vid);
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], xxlink_pid);
LOG_DEBUG("xxlink_vid:%x,xxlink_pid:%x",xxlink_vid,xxlink_pid);
} else
LOG_WARNING("incomplete ft232r_vid_pid configuration");
return ERROR_OK;
}
static const struct command_registration xxlink_subcommand_handlers[] = {
{
.name = "vid_pid",
.handler = xxlink_handle_vid_pid_command,
.mode = COMMAND_CONFIG,
.help = "USB VID and PID of the adapter",
.usage = "vid pid",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration xxlink_command_handlers[] = {
{
.name = "xxlink",
.mode = COMMAND_ANY,
.help = "perform xxlink management",
.chain = xxlink_subcommand_handlers,
.usage = "",
},
COMMAND_REGISTRATION_DONE
};
static struct jtag_interface xxlink_interface = {
.supported = DEBUG_CAP_TMS_SEQ,
//.execute_queue = syncbb_execute_queue,
};
struct adapter_driver xxlink_adapter_driver = {
.name = "xxlink",
.transports = jtag_only,
.commands = xxlink_command_handlers,
.init = xxlink_init,
.quit = xxlink_quit,
.speed = xxlink_speed,
.khz = xxlink_khz,
.speed_div = xxlink_speed_div,
.jtag_ops = &xxlink_interface,
};
对应的打印如下
Debug: 25 137 xxlink.c:107 xxlink_handle_vid_pid_command(): xxlink_vid:1122,xxlink_pid:3344
以上我们分享了如何给openocd添加一个驱动实现,了解了openocd是如何匹配我们的驱动,以及如何给驱动传递参数的。为后续驱动的实现做好了准备。