#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define DEV_NAME "fei_dev" // Device name
#define BUFFER_SIZE 32 // Size of the FIFO buffer
// Define a kernel FIFO and a mutex for synchronization
DEFINE_KFIFO(FIFOBuffer, char, BUFFER_SIZE);
struct mutex BufferMutex;
// Device related variables
static struct device *misc_device;
wait_queue_head_t read_queue; // For blocking reads when FIFO is empty
wait_queue_head_t write_queue; // For blocking writes when FIFO is full
void show_fifo_content(void)
{
char buf[BUFFER_SIZE];
unsigned int copied;
mutex_lock(&BufferMutex);
copied = kfifo_out_peek(&FIFOBuffer, buf, BUFFER_SIZE);
mutex_unlock(&BufferMutex);
buf[copied] = '\0'; // Null-terminate the string
printk(KERN_INFO "fei_dev: KFIFO content: %s\n", buf);
}
// Device open function
static int device_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "fei_dev: Device opened\n");
show_fifo_content();
return 0;
}
// Device release function
static int device_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "fei_dev: Device released\n");
return 0;
}
// Device read function
static ssize_t device_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
int read = 0, ret = 0;
// Lock the FIFO, read from it and then unlock
mutex_lock(&BufferMutex);
if (kfifo_is_empty(&FIFOBuffer))
{
mutex_unlock(&BufferMutex);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN; // Return EAGAIN if non-blocking mode
printk(KERN_INFO "fei_dev: Read process is blocked, needs space: %zu, available space: %u\n",
count, kfifo_len(&FIFOBuffer));
wait_event_interruptible(read_queue, !kfifo_is_empty(&FIFOBuffer));
mutex_lock(&BufferMutex); // Reacquire lock after being woken up
}
ret = kfifo_to_user(&FIFOBuffer, buf, count, &read);
mutex_unlock(&BufferMutex);
// If FIFO is not full, wake up any blocked write process
if (!kfifo_is_full(&FIFOBuffer))
{
printk(KERN_INFO "fei_dev: Wake up write process\n");
wake_up_interruptible(&write_queue);
}
show_fifo_content();
return read ? read : ret;
}
// Device write function
static ssize_t device_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int write = 0, ret = 0;
// Lock the FIFO, write to it and then unlock
mutex_lock(&BufferMutex);
if (kfifo_is_full(&FIFOBuffer))
{
mutex_unlock(&BufferMutex);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN; // Return EAGAIN if non-blocking mode
printk(KERN_INFO "fei_dev: Write process is blocked, needs space: %zu, available space: %u\n",
count, BUFFER_SIZE - kfifo_len(&FIFOBuffer));
wait_event_interruptible(write_queue, !kfifo_is_full(&FIFOBuffer));
mutex_lock(&BufferMutex); // Reacquire lock after being woken up
}
ret = kfifo_from_user(&FIFOBuffer, buf, count, &write);
mutex_unlock(&BufferMutex);
// If FIFO is not empty, wake up any blocked read process
if (!kfifo_is_empty(&FIFOBuffer))
{
printk(KERN_INFO "fei_dev: Wake up read process\n");
wake_up_interruptible(&read_queue);
}
show_fifo_content();
return write ? write : ret;
}
// File operations structure
static const struct file_operations DevFops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_release,
.read = device_read,
.write = device_write,
};
// Misc device structure
static struct miscdevice miscDeviceFIFOBlock = {
.minor = MISC_DYNAMIC_MINOR, // Dynamic minor number
.name = DEV_NAME, // Device name
.fops = &DevFops, // File operations structure
};
// Module initialization function
static int __init device_init(void)
{
// Register the misc device
int ret = misc_register(&miscDeviceFIFOBlock);
if (ret < 0)
{
printk(KERN_ERR "fei_dev: Failed to init\n");
return ret;
}
// Initialize the mutex
mutex_init(&BufferMutex);
// Set the device
misc_device = miscDeviceFIFOBlock.this_device;
// Initialize the wait queues for read and write
init_waitqueue_head(&read_queue);
init_waitqueue_head(&write_queue);
printk(KERN_INFO "fei_dev: Init successfully\n");
return ret;
}
// Module exit function
static void __exit device_exit(void)
{
// Unregister the misc device
misc_deregister(&miscDeviceFIFOBlock);
printk(KERN_INFO "fei_dev: Exited\n");
}
// Specify the init and exit functions
module_init(device_init);
module_exit(device_exit);
// Module metadata
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dekrt");
MODULE_DESCRIPTION("A simple non-blocking driver for testing KFIFO");
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define DEV_PATH "/dev/fei_dev"
#define BUFFER_SIZE 32
void write_to_device(const char *data, int non_block)
{
int fd;
ssize_t ret;
// Open the device file
if (non_block)
fd = open(DEV_PATH, O_WRONLY | O_NONBLOCK);
else
fd = open(DEV_PATH, O_WRONLY);
if (fd == -1)
{
perror("Failed to open device");
exit(EXIT_FAILURE);
}
// Write data to the device
ret = write(fd, data, strlen(data));
if (ret == -1)
{
if (errno == EAGAIN)
printf("Write operation would block\n");
else
perror("Failed to write to device");
}
else
{
printf("Wrote %ld bytes to the device: %s\n", ret, data);
}
// Close the device file
close(fd);
}
int main(int argc, char *argv[])
{
const char *data = "Hello, KFIFO!";
int non_block = 0;
if (argc > 1 && strcmp(argv[1], "nonblock") == 0)
{
non_block = 1;
}
write_to_device(data, non_block);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define DEV_PATH "/dev/fei_dev"
#define BUFFER_SIZE 32
void read_from_device(int non_block)
{
int fd;
ssize_t ret;
char buffer[BUFFER_SIZE + 1]; // +1 for null terminator
// Open the device file
if (non_block)
fd = open(DEV_PATH, O_RDONLY | O_NONBLOCK);
else
fd = open(DEV_PATH, O_RDONLY);
if (fd == -1)
{
perror("Failed to open device");
exit(EXIT_FAILURE);
}
// Read data from the device
ret = read(fd, buffer, BUFFER_SIZE);
if (ret == -1)
{
if (errno == EAGAIN)
printf("Read operation would block\n");
else
perror("Failed to read from device");
}
else
{
buffer[ret] = '\0'; // Null-terminate the string
printf("Read %ld bytes from the device: %s\n", ret, buffer);
}
// Close the device file
close(fd);
}
int main(int argc, char *argv[])
{
int non_block = 0;
if (argc > 1 && strcmp(argv[1], "nonblock") == 0)
{
non_block = 1;
}
read_from_device(non_block);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define DEV_PATH "/dev/fei_dev"
#define BUFFER_SIZE 32
#define DATA "Hello, KFIFO!"
void write_to_device(const char *data, int non_block)
{
int fd;
ssize_t ret;
size_t data_len = strlen(data);
size_t total_written = 0;
// Open the device file
if (non_block)
fd = open(DEV_PATH, O_WRONLY | O_NONBLOCK);
else
fd = open(DEV_PATH, O_WRONLY);
if (fd == -1)
{
perror("Failed to open device");
exit(EXIT_FAILURE);
}
// Write data to the device until the buffer is full
while (1)
{
ret = write(fd, data, data_len);
if (ret == -1)
{
if (errno == EAGAIN)
{
printf("Write operation would block\n");
if (non_block)
{
usleep(100000); // Sleep for 100ms in non-blocking mode before retrying
continue;
}
}
else
{
perror("Failed to write to device");
close(fd);
exit(EXIT_FAILURE);
}
}
else
{
printf("Wrote %ld bytes to the device: %s\n", ret, data);
total_written += ret;
if (total_written >= BUFFER_SIZE)
{
printf("Buffer is filled. Exiting...\n");
break;
}
}
}
// Close the device file
close(fd);
}
int main(int argc, char *argv[])
{
int non_block = 0;
if (argc > 1 && strcmp(argv[1], "nonblock") == 0)
{
non_block = 1;
}
write_to_device(DATA, non_block);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define DEV_PATH "/dev/fei_dev"
#define BUFFER_SIZE 32
void read_from_device(int non_block)
{
int fd;
ssize_t ret;
char buffer[BUFFER_SIZE + 1]; // +1 for null terminator
int total_read = 0;
// Open the device file
if (non_block)
fd = open(DEV_PATH, O_RDONLY | O_NONBLOCK);
else
fd = open(DEV_PATH, O_RDONLY);
if (fd == -1)
{
perror("Failed to open device");
exit(EXIT_FAILURE);
}
// Read data from the device until no more data is available
while (1)
{
ret = read(fd, buffer, BUFFER_SIZE);
if (ret == -1)
{
if (errno == EAGAIN)
{
printf("Read operation would block\n");
if (non_block)
{
usleep(100000); // Sleep for 100ms in non-blocking mode before retrying
continue;
}
}
else
{
perror("Failed to read from device");
close(fd);
exit(EXIT_FAILURE);
}
}
else if (ret == 0) // End of file reached
{
printf("End of file reached. Exiting...\n");
break;
}
else
{
buffer[ret] = '\0'; // Null-terminate the string
printf("Read %ld bytes from the device: %s\n", ret, buffer);
total_read += ret;
}
}
// Close the device file
close(fd);
}
int main(int argc, char *argv[])
{
int non_block = 0;
if (argc > 1 && strcmp(argv[1], "nonblock") == 0)
{
non_block = 1;
}
read_from_device(non_block);
return 0;
}