Project 1 - Develop the full featured char driver as a loaded module
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/semaphore.h>
#define CHAR_DRIVER_NAME "char_driver"
#define PROC_FILENAME "char_driver_size"
MODULE_LICENSE("GPL");
// Module parameters
static int max_memory_size = 8192; // Default value
// Device structure
struct char_driver_dev {
struct semaphore sem;
char *data;
size_t size;
loff_t offset;
int is_open;
};
static struct char_driver_dev *char_dev;
static int char_driver_open(struct inode *inode, struct file *filp)
{
if (down_interruptible(&char_dev->sem))
return -ERESTARTSYS;
if (char_dev->is_open) {
up(&char_dev->sem);
return -EBUSY;
}
char_dev->is_open = 1;
char_dev->offset = 0;
up(&char_dev->sem);
return 0;
}
static int char_driver_release(struct inode *inode, struct file *filp)
{
if (down_interruptible(&char_dev->sem))
return -ERESTARTSYS;
char_dev->is_open = 0;
up(&char_dev->sem);
return 0;
}
static ssize_t char_driver_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
// Implementation of read operation
}
static ssize_t char_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
// Implementation of write operation
}
static loff_t char_driver_llseek(struct file *filp, loff_t offset, int whence)
{
// Implementation of seek operation
}
static const struct file_operations char_driver_fops = {
.owner = THIS_MODULE,
.open = char_driver_open,
.release = char_driver_release,
.read = char_driver_read,
.write = char_driver_write,
.llseek = char_driver_llseek,
};
// Procfs functions
static int char_driver_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "Memory size allocated: %zu bytes\n", char_dev->size);
return 0;
}
static int char_driver_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, char_driver_proc_show, NULL);
}
static const struct file_operations char_driver_proc_fops = {
.owner = THIS_MODULE,
.open = char_driver_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init char_driver_init(void)
{
dev_t dev = 0;
int result;
// Register char device
result = alloc_chrdev_region(&dev, 0, 1, CHAR_DRIVER_NAME);
if (result < 0) {
printk(KERN_WARNING "char_driver: can't get major %d\n", MAJOR(dev));
return result;
}
// Initialize device structure
char_dev = kmalloc(sizeof(struct char_driver_dev), GFP_KERNEL);
if (!char_dev) {
result = -ENOMEM;
goto fail_malloc;
}
char_dev->data = kmalloc(max_memory_size, GFP_KERNEL);
if (!char_dev->data) {
result = -ENOMEM;
goto fail_malloc_data;
}
char_dev->size = max_memory_size;
char_dev->is_open = 0;
char_dev->offset = 0;
sema_init(&char_dev->sem, 1);
// Initialize char device
cdev_init(&char_dev->cdev, &char_driver_fops);
char_dev->cdev.owner = THIS_MODULE;
result = cdev_add(&char_dev->cdev, dev, 1);
if (result) {
printk(KERN_WARNING "char_driver: can't add cdev to region\n");
goto fail_cdev_add;
}
// Create procfs entry
proc_create(PROC_FILENAME, 0, NULL, &char_driver_proc_fops);
printk(KERN_INFO "char_driver: module loaded\n");
return 0;
fail_cdev_add:
kfree(char_dev->data);
fail_malloc_data:
kfree(char_dev);
fail_malloc:
unregister_chrdev_region(dev, 1);
return result;
}
static void __exit char_driver_exit(void)
{
dev_t dev = 0;
dev = MKDEV(MAJOR(dev), MINOR(dev));
unregister_chrdev_region(dev, 1);
cdev_del(&char_dev->cdev);
kfree(char_dev->data);
kfree(char_dev);
remove_proc_entry(PROC_FILENAME, NULL);
printk(KERN_INFO "char_driver: module unloaded\n");
}
module_init(char_driver_init);
module_exit(char_driver_exit);