/** * This file is part of the Xenomai project. * * @note Copyright (C) 2009 Philippe Gerum * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "internal.h" MODULE_DESCRIPTION("Real-time IPC interface"); MODULE_AUTHOR("Philippe Gerum "); MODULE_LICENSE("GPL"); static struct rtipc_protocol *protocols[IPCPROTO_MAX] = { #ifdef CONFIG_XENO_DRIVERS_RTIPC_XDDP [IPCPROTO_XDDP - 1] = &xddp_proto_driver, #endif #ifdef CONFIG_XENO_DRIVERS_RTIPC_IDDP [IPCPROTO_IDDP - 1] = &iddp_proto_driver, #endif #ifdef CONFIG_XENO_DRIVERS_RTIPC_BUFP [IPCPROTO_BUFP - 1] = &bufp_proto_driver, #endif }; DEFINE_XNPTREE(rtipc_ptree, "rtipc"); int rtipc_get_arg(struct rtdm_fd *fd, void *dst, const void *src, size_t len) { if (!rtdm_fd_is_user(fd)) { memcpy(dst, src, len); return 0; } return rtdm_copy_from_user(fd, dst, src, len); } int rtipc_put_arg(struct rtdm_fd *fd, void *dst, const void *src, size_t len) { if (!rtdm_fd_is_user(fd)) { memcpy(dst, src, len); return 0; } return rtdm_copy_to_user(fd, dst, src, len); } int rtipc_get_sockaddr(struct rtdm_fd *fd, struct sockaddr_ipc **saddrp, const void *arg) { const struct _rtdm_setsockaddr_args *p; struct _rtdm_setsockaddr_args sreq; int ret; if (!rtdm_fd_is_user(fd)) { p = arg; if (p->addrlen > 0) { if (p->addrlen != sizeof(**saddrp)) return -EINVAL; memcpy(*saddrp, p->addr, sizeof(**saddrp)); } else { if (p->addr) return -EINVAL; *saddrp = NULL; } return 0; } #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { struct compat_rtdm_setsockaddr_args csreq; ret = rtdm_safe_copy_from_user(fd, &csreq, arg, sizeof(csreq)); if (ret) return ret; if (csreq.addrlen > 0) { if (csreq.addrlen != sizeof(**saddrp)) return -EINVAL; return rtdm_safe_copy_from_user(fd, *saddrp, compat_ptr(csreq.addr), sizeof(**saddrp)); } if (csreq.addr) return -EINVAL; *saddrp = NULL; return 0; } #endif ret = rtdm_safe_copy_from_user(fd, &sreq, arg, sizeof(sreq)); if (ret) return ret; if (sreq.addrlen > 0) { if (sreq.addrlen != sizeof(**saddrp)) return -EINVAL; return rtdm_safe_copy_from_user(fd, *saddrp, sreq.addr, sizeof(**saddrp)); } if (sreq.addr) return -EINVAL; *saddrp = NULL; return 0; } int rtipc_put_sockaddr(struct rtdm_fd *fd, void *arg, const struct sockaddr_ipc *saddr) { const struct _rtdm_getsockaddr_args *p; struct _rtdm_getsockaddr_args sreq; socklen_t len; int ret; if (!rtdm_fd_is_user(fd)) { p = arg; if (*p->addrlen < sizeof(*saddr)) return -EINVAL; memcpy(p->addr, saddr, sizeof(*saddr)); *p->addrlen = sizeof(*saddr); return 0; } #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { struct compat_rtdm_getsockaddr_args csreq; ret = rtdm_safe_copy_from_user(fd, &csreq, arg, sizeof(csreq)); if (ret) return ret; ret = rtdm_safe_copy_from_user(fd, &len, compat_ptr(csreq.addrlen), sizeof(len)); if (ret) return ret; if (len < sizeof(*saddr)) return -EINVAL; ret = rtdm_safe_copy_to_user(fd, compat_ptr(csreq.addr), saddr, sizeof(*saddr)); if (ret) return ret; len = sizeof(*saddr); return rtdm_safe_copy_to_user(fd, compat_ptr(csreq.addrlen), &len, sizeof(len)); } #endif sreq.addr = NULL; sreq.addrlen = NULL; ret = rtdm_safe_copy_from_user(fd, &sreq, arg, sizeof(sreq)); if (ret) return ret; ret = rtdm_safe_copy_from_user(fd, &len, sreq.addrlen, sizeof(len)); if (ret) return ret; if (len < sizeof(*saddr)) return -EINVAL; ret = rtdm_safe_copy_to_user(fd, sreq.addr, saddr, sizeof(*saddr)); if (ret) return ret; len = sizeof(*saddr); return rtdm_safe_copy_to_user(fd, sreq.addrlen, &len, sizeof(len)); } int rtipc_get_sockoptout(struct rtdm_fd *fd, struct _rtdm_getsockopt_args *sopt, const void *arg) { if (!rtdm_fd_is_user(fd)) { *sopt = *(struct _rtdm_getsockopt_args *)arg; return 0; } #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { struct compat_rtdm_getsockopt_args csopt; int ret; ret = rtdm_safe_copy_from_user(fd, &csopt, arg, sizeof(csopt)); if (ret) return ret; sopt->level = csopt.level; sopt->optname = csopt.optname; sopt->optval = compat_ptr(csopt.optval); sopt->optlen = compat_ptr(csopt.optlen); return 0; } #endif return rtdm_safe_copy_from_user(fd, sopt, arg, sizeof(*sopt)); } int rtipc_put_sockoptout(struct rtdm_fd *fd, void *arg, const struct _rtdm_getsockopt_args *sopt) { if (!rtdm_fd_is_user(fd)) { *(struct _rtdm_getsockopt_args *)arg = *sopt; return 0; } #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { struct compat_rtdm_getsockopt_args csopt; int ret; csopt.level = sopt->level; csopt.optname = sopt->optname; csopt.optval = ptr_to_compat(sopt->optval); csopt.optlen = ptr_to_compat(sopt->optlen); ret = rtdm_safe_copy_to_user(fd, arg, &csopt, sizeof(csopt)); if (ret) return ret; return 0; } #endif return rtdm_safe_copy_to_user(fd, arg, sopt, sizeof(*sopt)); } int rtipc_get_sockoptin(struct rtdm_fd *fd, struct _rtdm_setsockopt_args *sopt, const void *arg) { if (!rtdm_fd_is_user(fd)) { *sopt = *(struct _rtdm_setsockopt_args *)arg; return 0; } #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { struct compat_rtdm_setsockopt_args csopt; int ret; ret = rtdm_safe_copy_from_user(fd, &csopt, arg, sizeof(csopt)); if (ret) return ret; sopt->level = csopt.level; sopt->optname = csopt.optname; sopt->optval = compat_ptr(csopt.optval); sopt->optlen = csopt.optlen; return 0; } #endif return rtdm_safe_copy_from_user(fd, sopt, arg, sizeof(*sopt)); } int rtipc_get_timeval(struct rtdm_fd *fd, struct __kernel_old_timeval *tv, const void *arg, size_t arglen) { #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { if (arglen != sizeof(struct old_timeval32)) return -EINVAL; return sys32_get_timeval(tv, arg); } #endif if (arglen != sizeof(*tv)) return -EINVAL; if (!rtdm_fd_is_user(fd)) { *tv = *(struct __kernel_old_timeval *)arg; return 0; } return rtdm_safe_copy_from_user(fd, tv, arg, sizeof(*tv)); } int rtipc_put_timeval(struct rtdm_fd *fd, void *arg, const struct __kernel_old_timeval *tv, size_t arglen) { #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { if (arglen != sizeof(struct old_timeval32)) return -EINVAL; return sys32_put_timeval(arg, tv); } #endif if (arglen != sizeof(*tv)) return -EINVAL; if (!rtdm_fd_is_user(fd)) { *(struct __kernel_old_timeval *)arg = *tv; return 0; } return rtdm_safe_copy_to_user(fd, arg, tv, sizeof(*tv)); } int rtipc_get_length(struct rtdm_fd *fd, size_t *lenp, const void *arg, size_t arglen) { #ifdef CONFIG_XENO_ARCH_SYS3264 if (rtdm_fd_is_compat(fd)) { const compat_size_t *csz; if (arglen != sizeof(*csz)) return -EINVAL; csz = arg; return csz == NULL || !access_rok(csz, sizeof(*csz)) || __xn_get_user(*lenp, csz) ? -EFAULT : 0; } #endif if (arglen != sizeof(size_t)) return -EINVAL; if (!rtdm_fd_is_user(fd)) { *lenp = *(size_t *)arg; return 0; } return rtdm_safe_copy_from_user(fd, lenp, arg, sizeof(*lenp)); } static int rtipc_socket(struct rtdm_fd *fd, int protocol) { struct rtipc_protocol *proto; struct rtipc_private *priv; int ret; if (protocol < 0 || protocol >= IPCPROTO_MAX) return -EPROTONOSUPPORT; if (protocol == IPCPROTO_IPC) /* Default protocol is IDDP */ protocol = IPCPROTO_IDDP; proto = protocols[protocol - 1]; if (proto == NULL) /* Not compiled in? */ return -ENOPROTOOPT; priv = rtdm_fd_to_private(fd); priv->proto = proto; priv->state = kmalloc(proto->proto_statesz, GFP_KERNEL); if (priv->state == NULL) return -ENOMEM; xnselect_init(&priv->send_block); xnselect_init(&priv->recv_block); ret = proto->proto_ops.socket(fd); if (ret) kfree(priv->state); return ret; } static void rtipc_close(struct rtdm_fd *fd) { struct rtipc_private *priv = rtdm_fd_to_private(fd); /* * CAUTION: priv->state shall be released by the * proto_ops.close() handler when appropriate (which may be * done asynchronously later, see XDDP). */ priv->proto->proto_ops.close(fd); xnselect_destroy(&priv->recv_block); xnselect_destroy(&priv->send_block); } static ssize_t rtipc_recvmsg(struct rtdm_fd *fd, struct user_msghdr *msg, int flags) { struct rtipc_private *priv = rtdm_fd_to_private(fd); return priv->proto->proto_ops.recvmsg(fd, msg, flags); } static ssize_t rtipc_sendmsg(struct rtdm_fd *fd, const struct user_msghdr *msg, int flags) { struct rtipc_private *priv = rtdm_fd_to_private(fd); return priv->proto->proto_ops.sendmsg(fd, msg, flags); } static ssize_t rtipc_read(struct rtdm_fd *fd, void *buf, size_t len) { struct rtipc_private *priv = rtdm_fd_to_private(fd); return priv->proto->proto_ops.read(fd, buf, len); } static ssize_t rtipc_write(struct rtdm_fd *fd, const void *buf, size_t len) { struct rtipc_private *priv = rtdm_fd_to_private(fd); return priv->proto->proto_ops.write(fd, buf, len); } static int rtipc_ioctl(struct rtdm_fd *fd, unsigned int request, void *arg) { struct rtipc_private *priv = rtdm_fd_to_private(fd); return priv->proto->proto_ops.ioctl(fd, request, arg); } static int rtipc_select(struct rtdm_fd *fd, struct xnselector *selector, unsigned int type, unsigned int index) { struct rtipc_private *priv = rtdm_fd_to_private(fd); struct xnselect_binding *binding; unsigned int pollstate, mask; struct xnselect *block; spl_t s; int ret; if (type != XNSELECT_READ && type != XNSELECT_WRITE) return -EINVAL; binding = xnmalloc(sizeof(*binding)); if (binding == NULL) return -ENOMEM; cobalt_atomic_enter(s); pollstate = priv->proto->proto_ops.pollstate(fd); if (type == XNSELECT_READ) { mask = pollstate & POLLIN; block = &priv->recv_block; } else { mask = pollstate & POLLOUT; block = &priv->send_block; } ret = xnselect_bind(block, binding, selector, type, index, mask); cobalt_atomic_leave(s); if (ret) xnfree(binding); return ret; } static struct rtdm_driver rtipc_driver = { .profile_info = RTDM_PROFILE_INFO(rtipc, RTDM_CLASS_RTIPC, RTDM_SUBCLASS_GENERIC, 1), .device_flags = RTDM_PROTOCOL_DEVICE, .device_count = 1, .context_size = sizeof(struct rtipc_private), .protocol_family = PF_RTIPC, .socket_type = SOCK_DGRAM, .ops = { .socket = rtipc_socket, .close = rtipc_close, .recvmsg_rt = rtipc_recvmsg, .recvmsg_nrt = NULL, .sendmsg_rt = rtipc_sendmsg, .sendmsg_nrt = NULL, .ioctl_rt = rtipc_ioctl, .ioctl_nrt = rtipc_ioctl, .read_rt = rtipc_read, .read_nrt = NULL, .write_rt = rtipc_write, .write_nrt = NULL, .select = rtipc_select, }, }; static struct rtdm_device device = { .driver = &rtipc_driver, .label = "rtipc", }; int __init __rtipc_init(void) { int ret, n; if (!rtdm_available()) return -ENOSYS; for (n = 0; n < IPCPROTO_MAX; n++) { if (protocols[n] && protocols[n]->proto_init) { ret = protocols[n]->proto_init(); if (ret) return ret; } } return rtdm_dev_register(&device); } void __exit __rtipc_exit(void) { int n; rtdm_dev_unregister(&device); for (n = 0; n < IPCPROTO_MAX; n++) { if (protocols[n] && protocols[n]->proto_exit) protocols[n]->proto_exit(); } } module_init(__rtipc_init); module_exit(__rtipc_exit);