2 * Inter process networking (virtual distributed ethernet) module
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>
33 #include "ipn_chrdev.h"
34 #include "ipn_msgbuf.h"
35 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
39 #define IPN_CHRDEV_PERSISTENT 1
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
48 unsigned int dev_count
;
49 struct ipn_network
*ipnn
;
51 struct class *devclass
;
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
);
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;*/
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
))
80 err
=ipn_node_create_connect(&ipn_node
, ipn_chrdev_to_ipnn
, inode
->i_cdev
);
81 filp
->private_data
=ipn_node
;
83 ipn_node
->shutdown
=mustshutdown
;
84 ipn_node
->chrdev
=inode
->i_rdev
;
89 /* file operations read/write/release/poll, mapped onto the
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
;
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
);
121 static int ipn_chrdev_ioctl(struct inode
*ino
, struct file
*filp
, unsigned int cmd
, unsigned long arg
)
123 static long ipn_chrdev_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
)
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
,
138 .ioctl
= ipn_chrdev_ioctl
140 .unlocked_ioctl
= ipn_chrdev_ioctl
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
)) )
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
)))
174 if (IS_ERR(fdevice
)) {
175 sysfs_unregister(devclass
,devr
->major
,devr
->minor
,minor_count
);
176 return ERR_PTR(PTR_ERR(fdevice
));
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
)
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
) {
202 dev_t dev
=MKDEV(devr
->major
,devr
->minor
);
203 if (!capable(CAP_MKNOD
))
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);*/
213 ret
=register_chrdev_region(dev
, devr
->count
, devr
->name
);
216 if ((ipnn
->chrdev
=kmalloc(sizeof(struct ipn_chrdev
), GFP_KERNEL
)) == NULL
) {
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
);
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
;
233 unregister_chrdev_free
:
236 unregister_chrdev_region(dev
, devr
->count
);
240 /* deregister a chrdev range. ipnn is locked */
241 int ipn_deregister_chrdev(struct ipn_network
*ipnn
) {
242 if (ipnn
->chrdev
== 0)
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
);
254 int ipn_chrdev_persistence(struct ipn_network
*ipnn
, int persistent
) {
255 if (ipnn
==NULL
|| ipnn
->chrdev
==NULL
)
258 ipnn
->chrdev
->flags
|= IPN_CHRDEV_PERSISTENT
;
260 ipnn
->chrdev
->flags
&= ~IPN_CHRDEV_PERSISTENT
;
264 int ipn_is_persistent_chrdev(struct ipn_network
*ipnn
) {
265 if (ipnn
==NULL
|| ipnn
->chrdev
==NULL
)
267 return (ipnn
->chrdev
->flags
& IPN_CHRDEV_PERSISTENT
)?1:0;