1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/version.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Renzo Davoli");
MODULE_DESCRIPTION("vpoll: sythesize events for select/poll/epoll");
MODULE_VERSION("0.01");
#define NAME "vpoll"
#define VPOLL_IOC_MAGIC '^'
#define VPOLL_IO_ADDEVENTS _IO(VPOLL_IOC_MAGIC, 1)
#define VPOLL_IO_DELEVENTS _IO(VPOLL_IOC_MAGIC, 2)
#define VPOLL_IO_SETEVENTS _IO(VPOLL_IOC_MAGIC, 3)
#define EPOLLALLMASK ((__force __poll_t)0x0fffffff)
static int major = -1;
static struct cdev mycdev;
static struct class *vpoll_class = NULL;
struct vpoll_data {
wait_queue_head_t wqh;
__poll_t events;
};
static int vpoll_open(struct inode *inode, struct file *file) {
struct vpoll_data *vpoll_data;
vpoll_data = kmalloc(sizeof(struct vpoll_data), GFP_KERNEL);
if (!vpoll_data)
return -ENOMEM;
vpoll_data->events = 0;
init_waitqueue_head(&vpoll_data->wqh);
file->private_data = vpoll_data;
return 0;
}
static int vpoll_release(struct inode *inode, struct file *file) {
struct vpoll_data *vpoll_data = file->private_data;
kfree(vpoll_data);
return 0;
}
static long vpoll_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
struct vpoll_data *vpoll_data = file->private_data;
__poll_t events = arg & EPOLLALLMASK;
long res = 0;
spin_lock_irq(&vpoll_data->wqh.lock);
switch(cmd) {
case VPOLL_IO_ADDEVENTS:
vpoll_data->events |= events;
break;
case VPOLL_IO_DELEVENTS:
vpoll_data->events &= ~events;
break;
case VPOLL_IO_SETEVENTS:
vpoll_data->events = events;
break;
default:
res = -EINVAL;
}
if (res >= 0) {
res = vpoll_data->events;
if (waitqueue_active(&vpoll_data->wqh))
wake_up_locked_poll(&vpoll_data->wqh, vpoll_data->events);
}
spin_unlock_irq(&vpoll_data->wqh.lock);
return res;
}
static __poll_t vpoll_poll(struct file *file, struct poll_table_struct *wait)
{
struct vpoll_data *vpoll_data = file->private_data;
poll_wait(file, &vpoll_data->wqh, wait);
return READ_ONCE(vpoll_data->events);
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = vpoll_open,
.release = vpoll_release,
.unlocked_ioctl = vpoll_ioctl,
.poll = vpoll_poll,
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
static char *vpoll_devnode(struct device *dev, umode_t *mode)
#else
static char *vpoll_devnode(const struct device *dev, umode_t *mode)
#endif
{
if (!mode)
return NULL;
*mode = 0666;
return NULL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
#define vpoll_class_create() class_create(NAME)
#else
#define vpoll_class_create() class_create(THIS_MODULE, NAME)
#endif
static int myinit(void)
{
int ret;
struct device *dev;
if ((ret = alloc_chrdev_region(&major, 0, 1, NAME)) < 0)
return ret;
vpoll_class = vpoll_class_create();
if (IS_ERR(vpoll_class)) {
ret = PTR_ERR(vpoll_class);
goto error_unregister_chrdev_region;
}
vpoll_class->devnode = vpoll_devnode;
dev = device_create(vpoll_class, NULL, major, NULL, NAME);
if (IS_ERR(dev)) {
ret = PTR_ERR(dev);
goto error_class_destroy;
}
cdev_init(&mycdev, &fops);
if ((ret = cdev_add(&mycdev, major, 1)) < 0)
goto error_device_destroy;
printk(KERN_INFO NAME ": loaded\n");
return 0;
error_device_destroy:
device_destroy(vpoll_class, major);
error_class_destroy:
class_destroy(vpoll_class);
error_unregister_chrdev_region:
unregister_chrdev_region(major, 1);
return ret;
}
static void myexit(void)
{
device_destroy(vpoll_class, major);
cdev_del(&mycdev);
class_destroy(vpoll_class);
unregister_chrdev_region(major, 1);
printk(KERN_INFO NAME ": unloaded\n");
}
module_init(myinit)
module_exit(myexit)
|