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>
32 #include "ipn_chrdev.h"
33 #include "ipn_msgbuf.h"
35 #define IPN_CHRDEV_PERSISTENT 1
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
44 unsigned int dev_count
;
45 struct ipn_network
*ipnn
;
47 struct class *devclass
;
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
);
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;*/
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
))
76 err
=ipn_node_create_connect(&ipn_node
, ipn_chrdev_to_ipnn
, inode
->i_cdev
);
77 filp
->private_data
=ipn_node
;
79 ipn_node
->shutdown
=mustshutdown
;
80 ipn_node
->chrdev
=inode
->i_rdev
;
85 /* file operations read/write/release/poll, mapped onto the
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
;
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
)) )
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
)))
162 if (IS_ERR(fdevice
)) {
163 sysfs_unregister(devclass
,devr
->major
,devr
->minor
,minor_count
);
164 return ERR_PTR(PTR_ERR(fdevice
));
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
)
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
) {
190 dev_t dev
=MKDEV(devr
->major
,devr
->minor
);
191 if (!capable(CAP_MKNOD
))
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);*/
201 ret
=register_chrdev_region(dev
, devr
->count
, devr
->name
);
204 if ((ipnn
->chrdev
=kmalloc(sizeof(struct ipn_chrdev
), GFP_KERNEL
)) == NULL
) {
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
);
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
;
221 unregister_chrdev_free
:
224 unregister_chrdev_region(dev
, devr
->count
);
228 /* deregister a chrdev range. ipnn is locked */
229 int ipn_deregister_chrdev(struct ipn_network
*ipnn
) {
230 if (ipnn
->chrdev
== 0)
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
);
242 int ipn_chrdev_persistence(struct ipn_network
*ipnn
, int persistent
) {
243 if (ipnn
==NULL
|| ipnn
->chrdev
==NULL
)
246 ipnn
->chrdev
->flags
|= IPN_CHRDEV_PERSISTENT
;
248 ipnn
->chrdev
->flags
&= ~IPN_CHRDEV_PERSISTENT
;
252 int ipn_is_persistent_chrdev(struct ipn_network
*ipnn
) {
253 if (ipnn
==NULL
|| ipnn
->chrdev
==NULL
)
255 return (ipnn
->chrdev
->flags
& IPN_CHRDEV_PERSISTENT
)?1:0;