epoll 字符设备驱动代码
时间: 2023-11-09 13:08:46 浏览: 105
字符设备驱动代码
下面是一个基于 epoll 的字符设备驱动代码示例:
```c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/err.h>
#include <linux/epoll.h>
#define MAX_DATA_LEN 4096
static struct cdev chr_dev;
static dev_t devno;
static struct semaphore sem;
static int open_count = 0;
static char *data_buf;
static size_t data_len = 0;
static int chrdev_open(struct inode *inode, struct file *filp)
{
if (down_interruptible(&sem) != 0) {
printk(KERN_WARNING "chrdev: interrupted while opening device\n");
return -ERESTARTSYS;
}
if (open_count != 0) {
printk(KERN_WARNING "chrdev: device is already opened\n");
up(&sem);
return -EBUSY;
}
open_count++;
up(&sem);
printk(KERN_INFO "chrdev: device opened\n");
return 0;
}
static int chrdev_release(struct inode *inode, struct file *filp)
{
if (down_interruptible(&sem) != 0) {
printk(KERN_WARNING "chrdev: interrupted while releasing device\n");
return -ERESTARTSYS;
}
open_count--;
up(&sem);
printk(KERN_INFO "chrdev: device released\n");
return 0;
}
static ssize_t chrdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
size_t read_len;
if (down_interruptible(&sem) != 0) {
printk(KERN_WARNING "chrdev: interrupted while reading from device\n");
return -ERESTARTSYS;
}
if (*f_pos >= data_len) {
up(&sem);
return 0;
}
read_len = min(count, data_len - *f_pos);
if (copy_to_user(buf, data_buf + *f_pos, read_len) != 0) {
up(&sem);
return -EFAULT;
}
*f_pos += read_len;
up(&sem);
printk(KERN_INFO "chrdev: read %zd bytes from device\n", read_len);
return read_len;
}
static ssize_t chrdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
if (down_interruptible(&sem) != 0) {
printk(KERN_WARNING "chrdev: interrupted while writing to device\n");
return -ERESTARTSYS;
}
if (count > MAX_DATA_LEN) {
printk(KERN_WARNING "chrdev: write length exceeds maximum allowed length\n");
up(&sem);
return -EINVAL;
}
if (copy_from_user(data_buf, buf, count) != 0) {
up(&sem);
return -EFAULT;
}
data_len = count;
up(&sem);
printk(KERN_INFO "chrdev: wrote %zd bytes to device\n", count);
return count;
}
static unsigned int chrdev_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(filp, wait, &wait->pt);
if (*f_pos < data_len) {
mask |= POLLIN | POLLRDNORM;
}
return mask;
}
static int chrdev_epoll_callback(struct file *filp, struct epoll_event *event)
{
if (event->events & EPOLLIN) {
event->events |= EPOLLIN;
}
return 0;
}
static struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.open = chrdev_open,
.release = chrdev_release,
.read = chrdev_read,
.write = chrdev_write,
.poll = chrdev_poll,
};
static int __init chrdev_init(void)
{
int err;
err = alloc_chrdev_region(&devno, 0, 1, "chrdev");
if (err < 0) {
printk(KERN_WARNING "chrdev: failed to allocate chrdev region\n");
return err;
}
cdev_init(&chr_dev, &chrdev_fops);
chr_dev.owner = THIS_MODULE;
chr_dev.ops = &chrdev_fops;
err = cdev_add(&chr_dev, devno, 1);
if (err < 0) {
printk(KERN_WARNING "chrdev: failed to add chrdev\n");
unregister_chrdev_region(devno, 1);
return err;
}
sema_init(&sem, 1);
data_buf = kmalloc(MAX_DATA_LEN, GFP_KERNEL);
if (data_buf == NULL) {
printk(KERN_WARNING "chrdev: failed to allocate data buffer\n");
cdev_del(&chr_dev);
unregister_chrdev_region(devno, 1);
return -ENOMEM;
}
printk(KERN_INFO "chrdev: initialized\n");
return 0;
}
static void __exit chrdev_exit(void)
{
kfree(data_buf);
cdev_del(&chr_dev);
unregister_chrdev_region(devno, 1);
printk(KERN_INFO "chrdev: exited\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A character device driver with epoll support");
```
上述代码实现了一个简单的字符设备驱动程序,支持打开、关闭、读取和写入操作,并使用了一个信号量来保护共享资源。此外,该驱动还支持 epoll,以便应用程序可以使用异步 I/O 进行通信。
阅读全文