一 : sendto()系统调用
本文基于 linux-4.9.38+Xenomai 3.0.5 的实时系统
加载用的是xenomai下pci总线驱动rtcan_adv_pci,以及rtcan_sja1000的can控制器驱动。
Xenomai 3 的rtdm驱动更像一般的Linux驱动,device会在/dev/rtdm/xxx创建一个设备文件。而用户空间使用时,写得来也和Linux的一般char设备相似,使用sendto/recvfrom/open/close/read/write/ioctl等系统调用,实际上只不过在link的时候这些函数都被做了手脚,替换成了libcobalt.so中的函数(参见 /usr/xenomai/lib/cobalt.wrappers)。
二:代码分析
在应用程序中通过 sendto()传入套接字生成的文件描述符,将会产生如下的调用过程。
rtdm_sendto()->rtdm_sendmsg()->rtdm_fd_sendmsg()->rtcan_raw_sendmsg()->rtcan_sja_start_xmit()->chip->write_reg()->iowrite8()
将应用程序中的数据内容复制到内核发送缓冲区中,再进行对硬件的发送:
rtdm.h中
static inline ssize_t rtdm_sendto(int s, const void *buf, size_t len,
int flags, const struct sockaddr *to,
socklen_t tolen)
{
struct user_msghdr msg;
struct iovec iov;
iov.iov_base = (void *)buf;
iov.iov_len = len;
msg.msg_name = (struct sockaddr *)to;
msg.msg_namelen = tolen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
return rtdm_sendmsg(s, &msg, flags);
}
static inline ssize_t rtdm_sendmsg(int s, const struct user_msghdr *msg, int flags)
{
return rtdm_fd_sendmsg(s, msg, flags);
}
在fd.c中
ssize_t rtdm_fd_sendmsg(int ufd, const struct user_msghdr *msg, int flags)
{
struct rtdm_fd *fd;
ssize_t ret;
fd = get_fd_fixup_mode(ufd);
if (IS_ERR(fd)) {
ret = PTR_ERR(fd);
goto out;
}
set_compat_bit(fd);
trace_cobalt_fd_sendmsg(current, fd, ufd, flags);
if (ipipe_root_p)
ret = fd->ops->sendmsg_nrt(fd, msg, flags);
else
ret = fd->ops->sendmsg_rt(fd, msg, flags);
if (!XENO_ASSERT(COBALT, !spltest()))
splnone();
rtdm_fd_put(fd);
out:
if (ret < 0)
trace_cobalt_fd_sendmsg_status(current, fd, ufd, ret);
return ret;
}
Xenomai 3.x protocol device description
(https://gitlab.denx.de/Xenomai/xenomai/-/wikis/Migrating_From_Xenomai_2_To_3)
rtcan_raw.c中
函数 fd->ops->sendmsg_rt(fd, msg, flags) = rtcan_raw_sendmsg()
static struct rtdm_driver rtcan_driver = {
.profile_info = RTDM_PROFILE_INFO(rtcan,
RTDM_CLASS_CAN,
RTDM_SUBCLASS_GENERIC,
RTCAN_PROFILE_VER),
.device_flags = RTDM_PROTOCOL_DEVICE,
.device_count = 1,
.context_size = sizeof(struct rtcan_socket),
.protocol_family = PF_CAN,
.socket_type = SOCK_RAW,
.ops = {
.socket = rtcan_raw_socket,
.close = rtcan_raw_close,
.ioctl_nrt = rtcan_raw_ioctl,
.recvmsg_rt = rtcan_raw_recvmsg,
.sendmsg_rt = rtcan_raw_sendmsg,
},
};
static struct rtdm_device rtcan_device = {
.driver = &rtcan_driver,
.label = "rtcan",
};
rtcan_raw.c中的
rtcan_raw_sendmsg()
ssize_t rtcan_raw_sendmsg(struct rtdm_fd *fd,
const struct user_msghdr *msg, int flags)
{
struct rtcan_socket *sock = rtdm_fd_to_private(fd);
struct sockaddr_can *scan = (struct sockaddr_can *)msg->msg_name;
struct sockaddr_can scan_buf;
struct iovec *iov = (struct iovec *)msg->msg_iov;
struct iovec iov_buf;
can_frame_t *frame;
can_frame_t frame_buf;
rtdm_lockctx_t lock_ctx;
nanosecs_rel_t timeout = 0;
struct tx_wait_queue tx_wait;
struct rtcan_device *dev;
int ifindex = 0;
int ret = 0;
spl_t s;
if (flags & MSG_OOB) /* Mirror BSD error message compatibility */
return -EOPNOTSUPP;
/* Only MSG_DONTWAIT is a valid flag. */
if (flags & ~MSG_DONTWAIT)
return -EINVAL;
/* Check msg_iovlen, only one buffer allowed */
if (msg->msg_iovlen != 1)