vde_autolink: Add missing null entry in getopt_long array.
[vde.git] / ipn / ipn_chrdev.c
blob8ef06e5b39dbc9142078e6b9588667f06b3e1f94
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 <linux/version.h>
32 #include "af_ipn.h"
33 #include "ipn_chrdev.h"
34 #include "ipn_msgbuf.h"
35 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
36 #define IPN_PRE2637
37 #endif
39 #define IPN_CHRDEV_PERSISTENT 1
40 /* struct ipn_chrdev:
41 * this is the type of the ipnn->chrdev field, when ipnn->chrdev is non-null,
42 * a character device (or a range of character devices) gives access to the ipn-network.
43 * this is also the data structure used by "container of" to retrieve ipnn from the device
44 * in O(1) time.
46 struct ipn_chrdev {
47 dev_t dev;
48 unsigned int dev_count;
49 struct ipn_network *ipnn;
50 struct cdev cdev;
51 struct class *devclass;
52 unsigned int flags;
55 /* cdev to ipnn mapping */
57 static struct ipn_network *ipn_chrdev_to_ipnn(void *i_cdev)
59 struct ipn_chrdev *chrdev;
60 chrdev=container_of(i_cdev, struct ipn_chrdev, cdev);
61 return chrdev->ipnn;
64 /* open: device open: find the ipnn and add a node to the ipnn */
65 static int ipn_chrdev_open(struct inode *inode, struct file *filp)
67 struct ipn_node *ipn_node;
68 /*struct ipn_network *ipnn;*/
69 int err;
70 unsigned char mustshutdown=0;
71 err = inode_permission(inode, MAY_READ);
72 if (!(filp->f_mode & FMODE_READ) || err == -EACCES || err == -EROFS)
73 mustshutdown|=RCV_SHUTDOWN;
74 err = inode_permission(inode, MAY_WRITE);
75 if (!(filp->f_mode & FMODE_WRITE) || err == -EACCES)
76 mustshutdown|=SEND_SHUTDOWN;
77 if ((mustshutdown & RCV_SHUTDOWN) && (mustshutdown & SEND_SHUTDOWN))
78 return -EACCES;
80 err=ipn_node_create_connect(&ipn_node, ipn_chrdev_to_ipnn, inode->i_cdev);
81 filp->private_data=ipn_node;
82 if (ipn_node) {
83 ipn_node->shutdown=mustshutdown;
84 ipn_node->chrdev=inode->i_rdev;
86 return err;
89 /* file operations read/write/release/poll, mapped onto the
90 * ipn_node ops */
91 static ssize_t ipn_chrdev_write(struct file *filp, const char __user *buf, size_t len, loff_t *f_pos)
93 struct ipn_node *ipn_node=filp->private_data;
94 struct iovec iov={(char __user *)buf,len};
95 //printk("ipn_chrdev_write %d\n",len);
96 return ipn_node_write(ipn_node, &iov, len);
99 static ssize_t ipn_chrdev_read(struct file *filp, char __user *buf, size_t len, loff_t *f_pos)
101 struct ipn_node *ipn_node=filp->private_data;
102 int noflags;
103 struct iovec iov={buf,len};
104 //printk("ipn_chrdev_read %d\n",len);
105 return ipn_node_read(ipn_node, &iov, len, &noflags, 0);
108 static int ipn_chrdev_release(struct inode *inode, struct file *filp)
110 struct ipn_node *ipn_node=filp->private_data;
111 return ipn_node_release(ipn_node);
114 static unsigned int ipn_chrdev_poll(struct file *filp, poll_table *wait)
116 struct ipn_node *ipn_node=filp->private_data;
117 return ipn_node_poll(ipn_node,filp,wait);
120 #ifdef IPN_PRE2637
121 static int ipn_chrdev_ioctl(struct inode *ino, struct file *filp, unsigned int cmd, unsigned long arg)
122 #else
123 static long ipn_chrdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
124 #endif
126 struct ipn_node *ipn_node=filp->private_data;
127 return ipn_node_ioctl(ipn_node,cmd,arg);
130 struct file_operations ipn_chrdev_fops = {
131 .owner = THIS_MODULE,
132 .open = ipn_chrdev_open,
133 .write = ipn_chrdev_write,
134 .read = ipn_chrdev_read,
135 .release = ipn_chrdev_release,
136 .poll = ipn_chrdev_poll,
137 #ifdef IPN_PRE2637
138 .ioctl = ipn_chrdev_ioctl
139 #else
140 .unlocked_ioctl = ipn_chrdev_ioctl
141 #endif
144 /* init a struct ipn_chrdev and add the cdev */
145 static int ipn_init_cdev(struct ipn_chrdev *ipn_dev, dev_t first, int count)
147 cdev_init(&ipn_dev->cdev, &ipn_chrdev_fops);
148 ipn_dev->cdev.owner = THIS_MODULE;
149 ipn_dev->cdev.ops = &ipn_chrdev_fops;
150 return cdev_add(&ipn_dev->cdev, first, count);
153 static void sysfs_unregister(struct class *devclass, unsigned int major, unsigned int minor, unsigned int count) {
154 unsigned int minor_count;
155 for (minor_count=0; minor_count<count; minor_count++)
156 device_destroy(devclass,MKDEV(major,minor+minor_count));
157 class_destroy(devclass);
160 static struct class *sysfs_register(struct chrdevreq *devr)
162 unsigned int minor_count;
163 struct class *devclass;
164 struct device *fdevice=NULL;
165 /* create a sysfs class for this device*/
166 if(IS_ERR(devclass=class_create(THIS_MODULE,devr->name)) )
167 return devclass;
168 for (minor_count=0; minor_count<devr->count; minor_count++){
169 unsigned int this_minor=devr->minor+minor_count;
170 if(IS_ERR(fdevice=device_create(devclass,NULL,MKDEV(devr->major,this_minor),
171 NULL,"%s%d",devr->name,this_minor)))
172 break;
174 if (IS_ERR(fdevice)) {
175 sysfs_unregister(devclass,devr->major,devr->minor,minor_count);
176 return ERR_PTR(PTR_ERR(fdevice));
178 return devclass;
181 static int chrdev_match(struct ipn_network *ipnn,void *arg)
183 struct chrdevreq *devr=arg;
184 if (ipnn->chrdev != NULL &&
185 MAJOR(ipnn->chrdev->dev) == devr->major &&
186 devr->minor >= MINOR(ipnn->chrdev->dev) &&
187 devr->minor < MINOR(ipnn->chrdev->dev) + ipnn->chrdev->dev_count)
188 return 1;
189 else
190 return 0;
193 struct ipn_network *ipn_find_chrdev(struct chrdevreq *devr)
195 return ipn_find_network_byfun(chrdev_match,devr);
198 /* register/allocate a chrdev range for this ipn network.
199 * ipnn is locked during this op */
200 int ipn_register_chrdev(struct ipn_network *ipnn, struct chrdevreq *devr) {
201 int ret;
202 dev_t dev=MKDEV(devr->major,devr->minor);
203 if (!capable(CAP_MKNOD))
204 return -EPERM;
205 if (ipnn->chrdev)
206 return -EADDRINUSE;
207 if (devr->major == 0) {
208 ret=alloc_chrdev_region(&dev, devr->minor, devr->count, devr->name);
209 devr->major=MAJOR(dev);
210 devr->minor=MINOR(dev);
211 /*printk("alloc_chrdev_region %d %d %s\n",ret,devr->major,devr->name);*/
212 } else
213 ret=register_chrdev_region(dev, devr->count, devr->name);
214 if (ret)
215 return ret;
216 if ((ipnn->chrdev=kmalloc(sizeof(struct ipn_chrdev), GFP_KERNEL)) == NULL) {
217 ret=-ENOMEM;
218 goto unregister_chrdev;
220 ipnn->chrdev->dev=dev;
221 ipnn->chrdev->dev_count=devr->count;
222 ipnn->chrdev->ipnn=ipnn;
223 ipnn->chrdev->flags=0;
224 ret=ipn_init_cdev(ipnn->chrdev, dev, devr->count);
225 if (ret)
226 goto unregister_chrdev_free;
227 if (IS_ERR(ipnn->chrdev->devclass=sysfs_register(devr))) {
228 ret=PTR_ERR(ipnn->chrdev->devclass);
229 goto unregister_chrdev_free;
231 return 0;
233 unregister_chrdev_free:
234 kfree(ipnn->chrdev);
235 unregister_chrdev:
236 unregister_chrdev_region(dev, devr->count);
237 return ret;
240 /* deregister a chrdev range. ipnn is locked */
241 int ipn_deregister_chrdev(struct ipn_network *ipnn) {
242 if (ipnn->chrdev == 0)
243 return EINVAL;
245 sysfs_unregister(ipnn->chrdev->devclass,MAJOR(ipnn->chrdev->dev),MINOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);
246 cdev_del(&ipnn->chrdev->cdev);
247 /*printk("deregister_chrdev %d %d\n",MAJOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);*/
248 unregister_chrdev_region(ipnn->chrdev->dev, ipnn->chrdev->dev_count);
249 kfree(ipnn->chrdev);
250 ipnn->chrdev=NULL;
251 return 0;
254 int ipn_chrdev_persistence(struct ipn_network *ipnn, int persistent) {
255 if (ipnn==NULL || ipnn->chrdev==NULL)
256 return EINVAL;
257 if (persistent)
258 ipnn->chrdev->flags |= IPN_CHRDEV_PERSISTENT;
259 else
260 ipnn->chrdev->flags &= ~IPN_CHRDEV_PERSISTENT;
261 return 0;
264 int ipn_is_persistent_chrdev(struct ipn_network *ipnn) {
265 if (ipnn==NULL || ipnn->chrdev==NULL)
266 return 0;
267 return (ipnn->chrdev->flags & IPN_CHRDEV_PERSISTENT)?1:0;