Basic support for libvirt (uml, qemu/kvm)
[vde.git] / ipn / ipn_chrdev.c
blobf86db3815b0d16a5960e4addbeee311018968751
1 /*
2 * Inter process networking (virtual distributed ethernet) module
3 * Char device support
5 * Copyright (C) 2009 Renzo Davoli, Jacopo Mondi, Enrico Tramacere
6 * {renzo,mondi,tramacer}@cs.unibo.it
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * Due to this file being licensed under the GPL there is controversy over
14 * whether this permits you to write a module that #includes this file
15 * without placing your module under the GPL. Please consult a lawyer for
16 * advice before doing this.
18 * WARNING: THIS CODE IS ALREADY EXPERIMENTAL
22 #include <linux/init.h>
23 #include <linux/module.h>
24 #include <linux/socket.h>
25 #include <linux/poll.h>
26 #include <linux/list.h>
27 #include <linux/mount.h>
28 #include <linux/device.h>
29 #include <linux/cdev.h>
30 #include <linux/string.h>
31 #include "af_ipn.h"
32 #include "ipn_chrdev.h"
33 #include "ipn_msgbuf.h"
35 #define IPN_CHRDEV_PERSISTENT 1
36 /* struct ipn_chrdev:
37 * this is the type of the ipnn->chrdev field, when ipnn->chrdev is non-null,
38 * a character device (or a range of character devices) gives access to the ipn-network.
39 * this is also the data structure used by "container of" to retrieve ipnn from the device
40 * in O(1) time.
42 struct ipn_chrdev {
43 dev_t dev;
44 unsigned int dev_count;
45 struct ipn_network *ipnn;
46 struct cdev cdev;
47 struct class *devclass;
48 unsigned int flags;
51 /* cdev to ipnn mapping */
53 static struct ipn_network *ipn_chrdev_to_ipnn(void *i_cdev)
55 struct ipn_chrdev *chrdev;
56 chrdev=container_of(i_cdev, struct ipn_chrdev, cdev);
57 return chrdev->ipnn;
60 /* open: device open: find the ipnn and add a node to the ipnn */
61 static int ipn_chrdev_open(struct inode *inode, struct file *filp)
63 struct ipn_node *ipn_node;
64 /*struct ipn_network *ipnn;*/
65 int err;
66 unsigned char mustshutdown=0;
67 err = inode_permission(inode, MAY_READ);
68 if (!(filp->f_mode & FMODE_READ) || err == -EACCES || err == -EROFS)
69 mustshutdown|=RCV_SHUTDOWN;
70 err = inode_permission(inode, MAY_WRITE);
71 if (!(filp->f_mode & FMODE_WRITE) || err == -EACCES)
72 mustshutdown|=SEND_SHUTDOWN;
73 if ((mustshutdown & RCV_SHUTDOWN) && (mustshutdown & SEND_SHUTDOWN))
74 return -EACCES;
76 err=ipn_node_create_connect(&ipn_node, ipn_chrdev_to_ipnn, inode->i_cdev);
77 filp->private_data=ipn_node;
78 if (ipn_node) {
79 ipn_node->shutdown=mustshutdown;
80 ipn_node->chrdev=inode->i_rdev;
82 return err;
85 /* file operations read/write/release/poll, mapped onto the
86 * ipn_node ops */
87 static ssize_t ipn_chrdev_write(struct file *filp, const char __user *buf, size_t len, loff_t *f_pos)
89 struct ipn_node *ipn_node=filp->private_data;
90 struct iovec iov={(char __user *)buf,len};
91 //printk("ipn_chrdev_write %d\n",len);
92 return ipn_node_write(ipn_node, &iov, len);
95 static ssize_t ipn_chrdev_read(struct file *filp, char __user *buf, size_t len, loff_t *f_pos)
97 struct ipn_node *ipn_node=filp->private_data;
98 int noflags;
99 struct iovec iov={buf,len};
100 //printk("ipn_chrdev_read %d\n",len);
101 return ipn_node_read(ipn_node, &iov, len, &noflags, 0);
104 static int ipn_chrdev_release(struct inode *inode, struct file *filp)
106 struct ipn_node *ipn_node=filp->private_data;
107 return ipn_node_release(ipn_node);
110 static unsigned int ipn_chrdev_poll(struct file *filp, poll_table *wait)
112 struct ipn_node *ipn_node=filp->private_data;
113 return ipn_node_poll(ipn_node,filp,wait);
116 static int ipn_chrdev_ioctl(struct inode *ino, struct file *filp, unsigned int cmd, unsigned long arg)
118 struct ipn_node *ipn_node=filp->private_data;
119 return ipn_node_ioctl(ipn_node,cmd,arg);
122 struct file_operations ipn_chrdev_fops = {
123 .owner = THIS_MODULE,
124 .open = ipn_chrdev_open,
125 .write = ipn_chrdev_write,
126 .read = ipn_chrdev_read,
127 .release = ipn_chrdev_release,
128 .poll = ipn_chrdev_poll,
129 .ioctl = ipn_chrdev_ioctl
132 /* init a struct ipn_chrdev and add the cdev */
133 static int ipn_init_cdev(struct ipn_chrdev *ipn_dev, dev_t first, int count)
135 cdev_init(&ipn_dev->cdev, &ipn_chrdev_fops);
136 ipn_dev->cdev.owner = THIS_MODULE;
137 ipn_dev->cdev.ops = &ipn_chrdev_fops;
138 return cdev_add(&ipn_dev->cdev, first, count);
141 static void sysfs_unregister(struct class *devclass, unsigned int major, unsigned int minor, unsigned int count) {
142 unsigned int minor_count;
143 for (minor_count=0; minor_count<count; minor_count++)
144 device_destroy(devclass,MKDEV(major,minor+minor_count));
145 class_destroy(devclass);
148 static struct class *sysfs_register(struct chrdevreq *devr)
150 unsigned int minor_count;
151 struct class *devclass;
152 struct device *fdevice;
153 /* create a sysfs class for this device*/
154 if(IS_ERR(devclass=class_create(THIS_MODULE,devr->name)) )
155 return devclass;
156 for (minor_count=0; minor_count<devr->count; minor_count++){
157 unsigned int this_minor=devr->minor+minor_count;
158 if(IS_ERR(fdevice=device_create(devclass,NULL,MKDEV(devr->major,this_minor),
159 NULL,"%s%d",devr->name,this_minor)))
160 break;
162 if (IS_ERR(fdevice)) {
163 sysfs_unregister(devclass,devr->major,devr->minor,minor_count);
164 return ERR_PTR(PTR_ERR(fdevice));
166 return devclass;
169 static int chrdev_match(struct ipn_network *ipnn,void *arg)
171 struct chrdevreq *devr=arg;
172 if (ipnn->chrdev != NULL &&
173 MAJOR(ipnn->chrdev->dev) == devr->major &&
174 devr->minor >= MINOR(ipnn->chrdev->dev) &&
175 devr->minor < MINOR(ipnn->chrdev->dev) + ipnn->chrdev->dev_count)
176 return 1;
177 else
178 return 0;
181 struct ipn_network *ipn_find_chrdev(struct chrdevreq *devr)
183 return ipn_find_network_byfun(chrdev_match,devr);
186 /* register/allocate a chrdev range for this ipn network.
187 * ipnn is locked during this op */
188 int ipn_register_chrdev(struct ipn_network *ipnn, struct chrdevreq *devr) {
189 int ret;
190 dev_t dev=MKDEV(devr->major,devr->minor);
191 if (!capable(CAP_MKNOD))
192 return -EPERM;
193 if (ipnn->chrdev)
194 return -EADDRINUSE;
195 if (devr->major == 0) {
196 ret=alloc_chrdev_region(&dev, devr->minor, devr->count, devr->name);
197 devr->major=MAJOR(dev);
198 devr->minor=MINOR(dev);
199 /*printk("alloc_chrdev_region %d %d %s\n",ret,devr->major,devr->name);*/
200 } else
201 ret=register_chrdev_region(dev, devr->count, devr->name);
202 if (ret)
203 return ret;
204 if ((ipnn->chrdev=kmalloc(sizeof(struct ipn_chrdev), GFP_KERNEL)) == NULL) {
205 ret=-ENOMEM;
206 goto unregister_chrdev;
208 ipnn->chrdev->dev=dev;
209 ipnn->chrdev->dev_count=devr->count;
210 ipnn->chrdev->ipnn=ipnn;
211 ipnn->chrdev->flags=0;
212 ret=ipn_init_cdev(ipnn->chrdev, dev, devr->count);
213 if (ret)
214 goto unregister_chrdev_free;
215 if (IS_ERR(ipnn->chrdev->devclass=sysfs_register(devr))) {
216 ret=PTR_ERR(ipnn->chrdev->devclass);
217 goto unregister_chrdev_free;
219 return 0;
221 unregister_chrdev_free:
222 kfree(ipnn->chrdev);
223 unregister_chrdev:
224 unregister_chrdev_region(dev, devr->count);
225 return ret;
228 /* deregister a chrdev range. ipnn is locked */
229 int ipn_deregister_chrdev(struct ipn_network *ipnn) {
230 if (ipnn->chrdev == 0)
231 return EINVAL;
233 sysfs_unregister(ipnn->chrdev->devclass,MAJOR(ipnn->chrdev->dev),MINOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);
234 cdev_del(&ipnn->chrdev->cdev);
235 /*printk("deregister_chrdev %d %d\n",MAJOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);*/
236 unregister_chrdev_region(ipnn->chrdev->dev, ipnn->chrdev->dev_count);
237 kfree(ipnn->chrdev);
238 ipnn->chrdev=NULL;
239 return 0;
242 int ipn_chrdev_persistence(struct ipn_network *ipnn, int persistent) {
243 if (ipnn==NULL || ipnn->chrdev==NULL)
244 return EINVAL;
245 if (persistent)
246 ipnn->chrdev->flags |= IPN_CHRDEV_PERSISTENT;
247 else
248 ipnn->chrdev->flags &= ~IPN_CHRDEV_PERSISTENT;
249 return 0;
252 int ipn_is_persistent_chrdev(struct ipn_network *ipnn) {
253 if (ipnn==NULL || ipnn->chrdev==NULL)
254 return 0;
255 return (ipnn->chrdev->flags & IPN_CHRDEV_PERSISTENT)?1:0;