IPROUTE2 功能太大,太全不方便使用
由于FLASH空间有限只设置CAN 需要一个小的程序只控制CAN 的状态。
相关改动主要代码已上传
PC 上测试
加载vcan内核模块: sudo modprobe vcan
创建虚拟CAN接口: sudo ip link add dev vcan0 type vcan
将虚拟CAN接口处于在线状态: sudo ip link set up vcan0
然后,通过命令ip addr | grep “can” 来验证是否可用并处于在线状态
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.youkuaiyun.com/xiandang8023/article/details/127990159
初步分析
设置CAN ip link set can0 type can bitrate 125000时
iplink.c -> do_iplink-> iplink_modify(RTM_NEWLINK, 0, argc-1, argv+1);
iplink_modify->rtnl_talk->__rtnl_talk(libnetlink.c)
最终是调用sendmsg 和recvmsg和底层通信。
static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
{
char *type = NULL;
struct iplink_req req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
.n.nlmsg_type = cmd,
.i.ifi_family = preferred_family,
};
int ret;
ret = iplink_parse(argc, argv, &req, &type);//获取argc 参数个数 argv指针 获取信息看下面分析 针对can 命令获取了 link 参数和 set 参数 初步判断返回&req 是设备名称
if (ret < 0)
return ret;
if (type) {
struct link_util *lu;
struct rtattr *linkinfo;
char *ulinep = strchr(type, '_');
int iflatype;
linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
strlen(type));
lu = get_link_kind(type);//根据type类型调用函数或库 can 应该是关联 iplink_can.c 的内容
/*如果只使用CAN 可以直接如下处理
#if 0
lu = get_link_kind(type);
#else
lu=&can_link_util;
#endif
*/
if (ulinep && !strcmp(ulinep, "_slave"))
iflatype = IFLA_INFO_SLAVE_DATA;
else
iflatype = IFLA_INFO_DATA;
argc -= ret;
argv += ret;
if (lu && lu->parse_opt && argc) {
struct rtattr *data;
data = addattr_nest(&req.n, sizeof(req), iflatype);//nlmsghdr 信息报添加信息
if (lu->parse_opt(lu, argc, argv, &req.n))//实际调用can_parse_opt 信息添加CAN设置信息 下面分析设置内容
return -1;
addattr_nest_end(&req.n, data);//完成nlmsghdr 相关设置
} else if (argc) {
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr,
"Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n",
*argv);
return -1;
}
addattr_nest_end(&req.n, linkinfo);
} else if (flags & NLM_F_CREATE) {
fprintf(stderr,
"Not enough information: \"type\" argument is required\n");
return -1;
}
//下面的rth 是从ip.c 中rtnl_open 中打开
if (echo_request)
ret = rtnl_echo_talk(&rth, &req.n, json, print_linkinfo);//调用设置并返回echo
else
ret = rtnl_talk(&rth, &req.n, NULL);//调用设置,不返回
if (ret)
return -2;
/* remove device from cache; next use can refresh with new data */
ll_drop_by_index(req.i.ifi_index);
return 0;
}
iplink_parse 中对应调用有
if (matches(*argv, "link") == 0) {
NEXT_ARG();
link = *argv;
。。。。
} else if (matches(*argv, "type") == 0) {
NEXT_ARG();
*type = *argv;
argc--; argv++;
break;
}
if (bt.bitrate || bt.tq)
addattr_l(n, 1024, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
if (dbt.bitrate || dbt.tq)
addattr_l(n, 1024, IFLA_CAN_DATA_BITTIMING, &dbt, sizeof(dbt));
if (cm.mask)
addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
if (tdcv != -1 || tdco != -1 || tdcf != -1) {
tdc = addattr_nest(n, 1024, IFLA_CAN_TDC | NLA_F_NESTED);
if (tdcv != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCV, tdcv);
if (tdco != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCO, tdco);
if (tdcf != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCF, tdcf);
addattr_nest_end(n, tdc);
}
static int can_parse_opt(struct link_util *lu, int argc, char **argv,
struct nlmsghdr *n){
。。。。。。
if (bt.bitrate || bt.tq)
addattr_l(n, 1024, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
if (dbt.bitrate || dbt.tq)
addattr_l(n, 1024, IFLA_CAN_DATA_BITTIMING, &dbt, sizeof(dbt));
if (cm.mask)
addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
if (tdcv != -1 || tdco != -1 || tdcf != -1) {
tdc = addattr_nest(n, 1024, IFLA_CAN_TDC | NLA_F_NESTED);
if (tdcv != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCV, tdcv);
if (tdco != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCO, tdco);
if (tdcf != -1)
addattr32(n, 1024, IFLA_CAN_TDC_TDCF, tdcf);
addattr_nest_end(n, tdc);
}
。。。。。。
获取类型的动态库
dlsym函数的功能就是可以从共享库(动态库)中获取符号(全局变量与函数符号)地址,通常用于获取函数符号地址,这样可用于对共享库中函数的包装;下面是函数原型及需要包含的头文件。
动态库
void *dlopen(const char *filename, int flag);
其中flag有:RTLD_LAZY RTLD_NOW RTLD_GLOBAL,其含义分别为:
RTLD_LAZY:在dlopen返回前,对于动态库中存在的未定义的变量(如外部变量extern,也可以是函数)不执行解析,就是不解析这个变量的地址。
-
RTLD_NOW:与上面不同,他需要在dlopen返回前,解析出每个未定义变量的地址,如果解析不出来,在dlopen会返回NULL,错误为:
- undefined symbol: xxxx…
RTLD_GLOBAL: 它的含义是使得库中的解析的定义变量在随后的其它的链接库中变得可以使用。
下面程序输出
/usr/lib/ip/link_can.so1
dlh == NULL
dlh 0x58b62e0
can_link_util
return 1
struct link_util *get_link_kind(const char *id)
{
void *dlh;
char buf[256];
struct link_util *l;
printf("%s\n",__FUNCTION__);
for (l = linkutil_list; l; l = l->next)//如果包里已经使用过了就不进行查找,没有进入后面进行相关的库查找。
if (strcmp(l->id, id) == 0)
return l;
snprintf(buf, sizeof(buf), "%s/link_%s.so", get_ip_lib_dir(), id);
printf( "%s/link_%s.so", get_ip_lib_dir(), id);
dlh = dlopen(buf, RTLD_LAZY);
if (dlh == NULL) {
printf( "1\n");
/* look in current binary, only open once */
dlh = BODY;
if (dlh == NULL) {
printf( "2\n");
dlh = BODY = dlopen(NULL, RTLD_LAZY);
if (dlh == NULL){
return NULL;
}
printf( "dlh 0x%x\n",(unsigned int )dlh);
}
}
printf( "%s_link_util\n", id);
snprintf(buf, sizeof(buf), "%s_link_util", id);
l = dlsym(dlh, buf);
if (l == NULL){
printf("dlsym =NULL \n");
return NULL;
}
printf("return 1 \n");
l->next = linkutil_list;
linkutil_list = l;
return l;
}
1直接在编译output 下修改可以不用改makefile
2IP 文件夹下MAKE FILE 修改
IPLINK 最主要修改
原文件是从 link*.so 对应设备的库中找到link_util结构体,只支持CAN那么直接返回对应结构体指针就好。
struct link_util *get_link_kind(const char *id)
{
if(strcmp(id,id)==0)
return &can_link_util;
else
printf( "Special versions olny support CAN \n");
}
去除了IPLINK_IOCTL_COMPAT 的支持
去除了 show、lst、list等不用上的内容。