/*** * * stack/rtnet_chrdev.c - implements char device for management interface * * 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 version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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 #include #include static DEFINE_SPINLOCK(ioctl_handler_lock); static LIST_HEAD(ioctl_handlers); static long rtnet_ioctl(struct file *file, unsigned int request, unsigned long arg) { struct rtnet_ioctl_head head; struct rtnet_device *rtdev = NULL; struct rtnet_ioctls *ioctls; struct list_head *entry; int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; ret = copy_from_user(&head, (void *)arg, sizeof(head)); if (ret != 0) return -EFAULT; spin_lock(&ioctl_handler_lock); list_for_each (entry, &ioctl_handlers) { ioctls = list_entry(entry, struct rtnet_ioctls, entry); if (ioctls->ioctl_type == _IOC_TYPE(request)) { atomic_inc(&ioctls->ref_count); spin_unlock(&ioctl_handler_lock); if ((_IOC_NR(request) & RTNET_IOC_NODEV_PARAM) == 0) { rtdev = rtdev_get_by_name(head.if_name); if (!rtdev) { atomic_dec(&ioctls->ref_count); return -ENODEV; } } ret = ioctls->handler(rtdev, request, arg); if (rtdev) rtdev_dereference(rtdev); atomic_dec(&ioctls->ref_count); return ret; } } spin_unlock(&ioctl_handler_lock); return -ENOTTY; } static int rtnet_core_ioctl(struct rtnet_device *rtdev, unsigned int request, unsigned long arg) { struct rtnet_core_cmd cmd; int ret; ret = copy_from_user(&cmd, (void *)arg, sizeof(cmd)); if (ret != 0) return -EFAULT; switch (request) { case IOC_RT_IFUP: ret = rtdev_up(rtdev, &cmd); break; case IOC_RT_IFDOWN: ret = rtdev_down(rtdev); break; case IOC_RT_IFINFO: if (cmd.args.info.ifindex > 0) rtdev = rtdev_get_by_index(cmd.args.info.ifindex); else rtdev = rtdev_get_by_name(cmd.head.if_name); if (rtdev == NULL) return -ENODEV; if (mutex_lock_interruptible(&rtdev->nrt_lock)) { rtdev_dereference(rtdev); return -ERESTARTSYS; } memcpy(cmd.head.if_name, rtdev->name, IFNAMSIZ); cmd.args.info.ifindex = rtdev->ifindex; cmd.args.info.type = rtdev->type; cmd.args.info.ip_addr = rtdev->local_ip; cmd.args.info.broadcast_ip = rtdev->broadcast_ip; cmd.args.info.mtu = rtdev->mtu; cmd.args.info.flags = rtdev->flags; if ((cmd.args.info.flags & IFF_UP) && (rtdev->link_state & (RTNET_LINK_STATE_PRESENT | RTNET_LINK_STATE_NOCARRIER)) == RTNET_LINK_STATE_PRESENT) cmd.args.info.flags |= IFF_RUNNING; memcpy(cmd.args.info.dev_addr, rtdev->dev_addr, MAX_ADDR_LEN); mutex_unlock(&rtdev->nrt_lock); rtdev_dereference(rtdev); if (copy_to_user((void *)arg, &cmd, sizeof(cmd)) != 0) return -EFAULT; break; default: ret = -ENOTTY; } return ret; } int rtnet_register_ioctls(struct rtnet_ioctls *ioctls) { struct list_head *entry; struct rtnet_ioctls *registered_ioctls; RTNET_ASSERT(ioctls->handler != NULL, return -EINVAL;); spin_lock(&ioctl_handler_lock); list_for_each (entry, &ioctl_handlers) { registered_ioctls = list_entry(entry, struct rtnet_ioctls, entry); if (registered_ioctls->ioctl_type == ioctls->ioctl_type) { spin_unlock(&ioctl_handler_lock); return -EEXIST; } } list_add_tail(&ioctls->entry, &ioctl_handlers); atomic_set(&ioctls->ref_count, 0); spin_unlock(&ioctl_handler_lock); return 0; } void rtnet_unregister_ioctls(struct rtnet_ioctls *ioctls) { spin_lock(&ioctl_handler_lock); while (atomic_read(&ioctls->ref_count) != 0) { spin_unlock(&ioctl_handler_lock); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1 * HZ); /* wait a second */ spin_lock(&ioctl_handler_lock); } list_del(&ioctls->entry); spin_unlock(&ioctl_handler_lock); } static struct file_operations rtnet_fops = { .owner = THIS_MODULE, .unlocked_ioctl = rtnet_ioctl, .compat_ioctl = rtnet_ioctl, }; static struct miscdevice rtnet_chr_misc_dev = { .minor = RTNET_MINOR, .name = "rtnet", .fops = &rtnet_fops, }; static struct rtnet_ioctls core_ioctls = { .service_name = "RTnet Core", .ioctl_type = RTNET_IOC_TYPE_CORE, .handler = rtnet_core_ioctl }; /** * rtnet_chrdev_init - * */ int __init rtnet_chrdev_init(void) { int err; err = misc_register(&rtnet_chr_misc_dev); if (err) { printk("RTnet: unable to register rtnet management device/class " "(error %d)\n", err); return err; } rtnet_register_ioctls(&core_ioctls); return 0; } /** * rtnet_chrdev_release - * */ void rtnet_chrdev_release(void) { misc_deregister(&rtnet_chr_misc_dev); } EXPORT_SYMBOL_GPL(rtnet_register_ioctls); EXPORT_SYMBOL_GPL(rtnet_unregister_ioctls);