ipn_chrdev: sysfs management of devices
[vde.git] / ipn / ipn_chrdev.c
blob19fd32ff272c8ff7c2e6bb2c561f9fc0b8fe2306
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 /* struct ipn_chrdev:
36 * this is the type of the ipnn->chrdev field, when ipnn->chrdev is non-null,
37 * a character device (or a range of character devices) gives access to the ipn-network.
38 * this is also the data structure used by "container of" to retrieve ipnn from the device
39 * in O(1) time.
41 struct ipn_chrdev {
42 dev_t dev;
43 unsigned int dev_count;
44 struct ipn_network *ipnn;
45 struct cdev cdev;
46 struct class *devclass;
47 unsigned int flags;
50 /* cdev to ipnn mapping */
52 static struct ipn_network *ipn_chrdev_to_ipnn(void *i_cdev)
54 struct ipn_chrdev *chrdev;
55 chrdev=container_of(i_cdev, struct ipn_chrdev, cdev);
56 return chrdev->ipnn;
59 /* open: device open: find the ipnn and add a node to the ipnn */
60 static int ipn_chrdev_open(struct inode *inode, struct file *filp)
62 struct ipn_node *ipn_node;
63 /*struct ipn_network *ipnn;*/
64 int err;
65 unsigned char mustshutdown=0;
66 err = inode_permission(inode, MAY_READ);
67 if (!(filp->f_mode & FMODE_READ) || err == -EACCES || err == -EROFS)
68 mustshutdown|=RCV_SHUTDOWN;
69 err = inode_permission(inode, MAY_WRITE);
70 if (!(filp->f_mode & FMODE_WRITE) || err == -EACCES)
71 mustshutdown|=SEND_SHUTDOWN;
72 if ((mustshutdown & RCV_SHUTDOWN) && (mustshutdown & SEND_SHUTDOWN))
73 return -EACCES;
75 err=ipn_node_create_connect(&ipn_node, ipn_chrdev_to_ipnn, inode->i_cdev);
76 filp->private_data=ipn_node;
77 if (ipn_node) {
78 ipn_node->shutdown=mustshutdown;
79 ipn_node->chrdev=inode->i_rdev;
81 return err;
84 /* file operations read/write/release/poll, mapped onto the
85 * ipn_node ops */
86 static ssize_t ipn_chrdev_write(struct file *filp, const char __user *buf, size_t len, loff_t *f_pos)
88 struct ipn_node *ipn_node=filp->private_data;
89 struct iovec iov={(char __user *)buf,len};
90 //printk("ipn_chrdev_write %d\n",len);
91 return ipn_node_write(ipn_node, &iov, len);
94 static ssize_t ipn_chrdev_read(struct file *filp, char __user *buf, size_t len, loff_t *f_pos)
96 struct ipn_node *ipn_node=filp->private_data;
97 int noflags;
98 struct iovec iov={buf,len};
99 //printk("ipn_chrdev_read %d\n",len);
100 return ipn_node_read(ipn_node, &iov, len, &noflags, 0);
103 static int ipn_chrdev_release(struct inode *inode, struct file *filp)
105 struct ipn_node *ipn_node=filp->private_data;
106 return ipn_node_release(ipn_node);
109 static unsigned int ipn_chrdev_poll(struct file *filp, poll_table *wait)
111 struct ipn_node *ipn_node=filp->private_data;
112 return ipn_node_poll(ipn_node,filp,wait);
115 static int ipn_chrdev_ioctl(struct inode *ino, struct file *filp, unsigned int cmd, unsigned long arg)
117 struct ipn_node *ipn_node=filp->private_data;
118 return ipn_node_ioctl(ipn_node,cmd,arg);
121 struct file_operations ipn_chrdev_fops = {
122 .owner = THIS_MODULE,
123 .open = ipn_chrdev_open,
124 .write = ipn_chrdev_write,
125 .read = ipn_chrdev_read,
126 .release = ipn_chrdev_release,
127 .poll = ipn_chrdev_poll,
128 .ioctl = ipn_chrdev_ioctl
131 /* init a struct ipn_chrdev and add the cdev */
132 static int ipn_init_cdev(struct ipn_chrdev *ipn_dev, dev_t first, int count)
134 cdev_init(&ipn_dev->cdev, &ipn_chrdev_fops);
135 ipn_dev->cdev.owner = THIS_MODULE;
136 ipn_dev->cdev.ops = &ipn_chrdev_fops;
137 return cdev_add(&ipn_dev->cdev, first, count);
140 static void sysfs_unregister(struct class *devclass, unsigned int major, unsigned int minor, unsigned int count) {
141 unsigned int minor_count;
142 for (minor_count=0; minor_count<count; minor_count++)
143 device_destroy(devclass,MKDEV(major,minor+minor_count));
144 class_destroy(devclass);
147 static struct class *sysfs_register(struct chrdevreq *devr)
149 unsigned int minor_count;
150 struct class *devclass;
151 struct device *fdevice;
152 /* create a sysfs class for this device*/
153 if(IS_ERR(devclass=class_create(THIS_MODULE,devr->name)) )
154 return devclass;
155 for (minor_count=0; minor_count<devr->count; minor_count++){
156 unsigned int this_minor=devr->minor+minor_count;
157 if(IS_ERR(fdevice=device_create(devclass,NULL,MKDEV(devr->major,this_minor),
158 NULL,"%s%d",devr->name,this_minor)))
159 break;
161 if (IS_ERR(fdevice)) {
162 sysfs_unregister(devclass,devr->major,devr->minor,minor_count);
163 return ERR_PTR(PTR_ERR(fdevice));
165 return devclass;
168 /* register/allocate a chrdev range for this ipn network.
169 * ipnn is locked during this op */
170 int ipn_register_chrdev(struct ipn_network *ipnn, struct chrdevreq *devr) {
171 int ret;
172 dev_t dev=MKDEV(devr->major,devr->minor);
173 if (!capable(CAP_MKNOD))
174 return -EPERM;
175 if (ipnn->chrdev)
176 return -EADDRINUSE;
177 if (devr->major == 0) {
178 ret=alloc_chrdev_region(&dev, devr->minor, devr->count, devr->name);
179 devr->major=MAJOR(dev);
180 devr->minor=MINOR(dev);
181 /*printk("alloc_chrdev_region %d %d %s\n",ret,devr->major,devr->name);*/
182 } else
183 ret=register_chrdev_region(dev, devr->count, devr->name);
184 if (ret)
185 return ret;
186 if ((ipnn->chrdev=kmalloc(sizeof(struct ipn_chrdev), GFP_KERNEL)) == NULL) {
187 ret=-ENOMEM;
188 goto unregister_chrdev;
190 ipnn->chrdev->dev=dev;
191 ipnn->chrdev->dev_count=devr->count;
192 ipnn->chrdev->ipnn=ipnn;
193 ipnn->chrdev->flags=0;
194 ret=ipn_init_cdev(ipnn->chrdev, dev, devr->count);
195 if (ret)
196 goto unregister_chrdev_free;
197 if (IS_ERR(ipnn->chrdev->devclass=sysfs_register(devr))) {
198 ret=PTR_ERR(ipnn->chrdev->devclass);
199 goto unregister_chrdev_free;
201 return 0;
203 unregister_chrdev_free:
204 kfree(ipnn->chrdev);
205 unregister_chrdev:
206 unregister_chrdev_region(dev, devr->count);
207 return ret;
210 /* deregister a chrdev range. ipnn is locked */
211 int ipn_deregister_chrdev(struct ipn_network *ipnn) {
212 if (ipnn->chrdev == 0)
213 return EINVAL;
215 sysfs_unregister(ipnn->chrdev->devclass,MAJOR(ipnn->chrdev->dev),MINOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);
216 cdev_del(&ipnn->chrdev->cdev);
217 /*printk("deregister_chrdev %d %d\n",MAJOR(ipnn->chrdev->dev),ipnn->chrdev->dev_count);*/
218 unregister_chrdev_region(ipnn->chrdev->dev, ipnn->chrdev->dev_count);
219 kfree(ipnn->chrdev);
220 ipnn->chrdev=NULL;
221 return 0;