/*** * * stack/socket.c - sockets implementation for rtnet * * Copyright (C) 1999 Lineo, Inc * 1999, 2002 David A. Schleef * 2002 Ulrich Marx * 2003-2005 Jan Kiszka * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define SKB_POOL_CLOSED 0 static unsigned int socket_rtskbs = DEFAULT_SOCKET_RTSKBS; module_param(socket_rtskbs, uint, 0444); MODULE_PARM_DESC(socket_rtskbs, "Default number of realtime socket buffers in socket pools"); /************************************************************************ * internal socket functions * ************************************************************************/ int rt_bare_socket_init(struct rtdm_fd *fd, unsigned short protocol, unsigned int priority, unsigned int pool_size) { struct rtsocket *sock = rtdm_fd_to_private(fd); int err; err = rtskb_pool_init(&sock->skb_pool, pool_size, NULL, fd); if (err < 0) return err; sock->protocol = protocol; sock->priority = priority; return err; } EXPORT_SYMBOL_GPL(rt_bare_socket_init); /*** * rt_socket_init - initialises a new socket structure */ int rt_socket_init(struct rtdm_fd *fd, unsigned short protocol) { struct rtsocket *sock = rtdm_fd_to_private(fd); unsigned int pool_size; sock->flags = 0; sock->callback_func = NULL; rtskb_queue_init(&sock->incoming); sock->timeout = 0; rtdm_lock_init(&sock->param_lock); rtdm_sem_init(&sock->pending_sem, 0); pool_size = rt_bare_socket_init(fd, protocol, RTSKB_PRIO_VALUE(SOCK_DEF_PRIO, RTSKB_DEF_RT_CHANNEL), socket_rtskbs); sock->pool_size = pool_size; mutex_init(&sock->pool_nrt_lock); if (pool_size < socket_rtskbs) { /* fix statistics */ if (pool_size == 0) rtskb_pools--; rt_socket_cleanup(fd); return -ENOMEM; } return 0; } EXPORT_SYMBOL_GPL(rt_socket_init); /*** * rt_socket_cleanup - releases resources allocated for the socket */ void rt_socket_cleanup(struct rtdm_fd *fd) { struct rtsocket *sock = rtdm_fd_to_private(fd); rtdm_sem_destroy(&sock->pending_sem); mutex_lock(&sock->pool_nrt_lock); set_bit(SKB_POOL_CLOSED, &sock->flags); if (sock->pool_size > 0) rtskb_pool_release(&sock->skb_pool); mutex_unlock(&sock->pool_nrt_lock); } EXPORT_SYMBOL_GPL(rt_socket_cleanup); /*** * rt_socket_common_ioctl */ int rt_socket_common_ioctl(struct rtdm_fd *fd, int request, void __user *arg) { struct rtsocket *sock = rtdm_fd_to_private(fd); int ret = 0; struct rtnet_callback *callback; const unsigned int *val; unsigned int _val; const nanosecs_rel_t *timeout; nanosecs_rel_t _timeout; rtdm_lockctx_t context; switch (request) { case RTNET_RTIOC_XMITPARAMS: val = rtnet_get_arg(fd, &_val, arg, sizeof(_val)); if (IS_ERR(val)) return PTR_ERR(val); sock->priority = *val; break; case RTNET_RTIOC_TIMEOUT: timeout = rtnet_get_arg(fd, &_timeout, arg, sizeof(_timeout)); if (IS_ERR(timeout)) return PTR_ERR(timeout); sock->timeout = *timeout; break; case RTNET_RTIOC_CALLBACK: if (rtdm_fd_is_user(fd)) return -EACCES; rtdm_lock_get_irqsave(&sock->param_lock, context); callback = arg; sock->callback_func = callback->func; sock->callback_arg = callback->arg; rtdm_lock_put_irqrestore(&sock->param_lock, context); break; case RTNET_RTIOC_EXTPOOL: val = rtnet_get_arg(fd, &_val, arg, sizeof(_val)); if (IS_ERR(val)) return PTR_ERR(val); if (rtdm_in_rt_context()) return -ENOSYS; mutex_lock(&sock->pool_nrt_lock); if (test_bit(SKB_POOL_CLOSED, &sock->flags)) { mutex_unlock(&sock->pool_nrt_lock); return -EBADF; } ret = rtskb_pool_extend(&sock->skb_pool, *val); sock->pool_size += ret; mutex_unlock(&sock->pool_nrt_lock); if (ret == 0 && *val > 0) ret = -ENOMEM; break; case RTNET_RTIOC_SHRPOOL: val = rtnet_get_arg(fd, &_val, arg, sizeof(_val)); if (IS_ERR(val)) return PTR_ERR(val); if (rtdm_in_rt_context()) return -ENOSYS; mutex_lock(&sock->pool_nrt_lock); ret = rtskb_pool_shrink(&sock->skb_pool, *val); sock->pool_size -= ret; mutex_unlock(&sock->pool_nrt_lock); if (ret == 0 && *val > 0) ret = -EBUSY; break; default: ret = -EOPNOTSUPP; break; } return ret; } EXPORT_SYMBOL_GPL(rt_socket_common_ioctl); /*** * rt_socket_if_ioctl */ int rt_socket_if_ioctl(struct rtdm_fd *fd, int request, void __user *arg) { struct rtnet_device *rtdev; struct ifreq _ifr, *ifr, *u_ifr; struct sockaddr_in _sin; struct ifconf _ifc, *ifc, *u_ifc; int ret = 0, size = 0, i; short flags; if (request == SIOCGIFCONF) { u_ifc = arg; ifc = rtnet_get_arg(fd, &_ifc, u_ifc, sizeof(_ifc)); if (IS_ERR(ifc)) return PTR_ERR(ifc); for (u_ifr = ifc->ifc_req, i = 1; i <= MAX_RT_DEVICES; i++, u_ifr++) { rtdev = rtdev_get_by_index(i); if (rtdev == NULL) continue; if ((rtdev->flags & IFF_UP) == 0) { rtdev_dereference(rtdev); continue; } size += sizeof(struct ifreq); if (size > ifc->ifc_len) { rtdev_dereference(rtdev); size = ifc->ifc_len; break; } ret = rtnet_put_arg(fd, u_ifr->ifr_name, rtdev->name, IFNAMSIZ); if (ret == 0) { memset(&_sin, 0, sizeof(_sin)); _sin.sin_family = AF_INET; _sin.sin_addr.s_addr = rtdev->local_ip; ret = rtnet_put_arg(fd, &u_ifr->ifr_addr, &_sin, sizeof(_sin)); } rtdev_dereference(rtdev); if (ret) return ret; } return rtnet_put_arg(fd, &u_ifc->ifc_len, &size, sizeof(size)); } u_ifr = arg; ifr = rtnet_get_arg(fd, &_ifr, u_ifr, sizeof(_ifr)); if (IS_ERR(ifr)) return PTR_ERR(ifr); if (request == SIOCGIFNAME) { rtdev = rtdev_get_by_index(ifr->ifr_ifindex); if (rtdev == NULL) return -ENODEV; ret = rtnet_put_arg(fd, u_ifr->ifr_name, rtdev->name, IFNAMSIZ); goto out; } rtdev = rtdev_get_by_name(ifr->ifr_name); if (rtdev == NULL) return -ENODEV; switch (request) { case SIOCGIFINDEX: ret = rtnet_put_arg(fd, &u_ifr->ifr_ifindex, &rtdev->ifindex, sizeof(u_ifr->ifr_ifindex)); break; case SIOCGIFFLAGS: flags = rtdev->flags; if ((ifr->ifr_flags & IFF_UP) && (rtdev->link_state & (RTNET_LINK_STATE_PRESENT | RTNET_LINK_STATE_NOCARRIER)) == RTNET_LINK_STATE_PRESENT) flags |= IFF_RUNNING; ret = rtnet_put_arg(fd, &u_ifr->ifr_flags, &flags, sizeof(u_ifr->ifr_flags)); break; case SIOCGIFHWADDR: ret = rtnet_put_arg(fd, &u_ifr->ifr_hwaddr.sa_data, rtdev->dev_addr, rtdev->addr_len); if (!ret) ret = rtnet_put_arg( fd, &u_ifr->ifr_hwaddr.sa_family, &rtdev->type, sizeof(u_ifr->ifr_hwaddr.sa_family)); break; case SIOCGIFADDR: memset(&_sin, 0, sizeof(_sin)); _sin.sin_family = AF_INET; _sin.sin_addr.s_addr = rtdev->local_ip; ret = rtnet_put_arg(fd, &u_ifr->ifr_addr, &_sin, sizeof(_sin)); break; case SIOCETHTOOL: if (rtdev->do_ioctl != NULL) { if (rtdm_in_rt_context()) return -ENOSYS; ret = rtdev->do_ioctl(rtdev, ifr, request); } else ret = -EOPNOTSUPP; break; case SIOCDEVPRIVATE ... SIOCDEVPRIVATE + 15: if (rtdev->do_ioctl != NULL) ret = rtdev->do_ioctl(rtdev, ifr, request); else ret = -EOPNOTSUPP; break; default: ret = -EOPNOTSUPP; break; } out: rtdev_dereference(rtdev); return ret; } EXPORT_SYMBOL_GPL(rt_socket_if_ioctl); int rt_socket_select_bind(struct rtdm_fd *fd, rtdm_selector_t *selector, enum rtdm_selecttype type, unsigned fd_index) { struct rtsocket *sock = rtdm_fd_to_private(fd); switch (type) { case XNSELECT_READ: return rtdm_sem_select(&sock->pending_sem, selector, XNSELECT_READ, fd_index); default: return -EBADF; } return -EINVAL; } EXPORT_SYMBOL_GPL(rt_socket_select_bind); void *rtnet_get_arg(struct rtdm_fd *fd, void *tmp, const void *src, size_t len) { int ret; if (!rtdm_fd_is_user(fd)) return (void *)src; ret = rtdm_copy_from_user(fd, tmp, src, len); if (ret) return ERR_PTR(ret); return tmp; } EXPORT_SYMBOL_GPL(rtnet_get_arg); int rtnet_put_arg(struct rtdm_fd *fd, void *dst, const void *src, size_t len) { if (!rtdm_fd_is_user(fd)) { if (dst != src) memcpy(dst, src, len); return 0; } return rtdm_copy_to_user(fd, dst, src, len); } EXPORT_SYMBOL_GPL(rtnet_put_arg);