SCULL (Simple Character Utility for Loading Localities) is a classic example of a character device driver in the Linux kernel, often used as an introductory project for learning Linux character device drivers. The design goal of the SCULL device is to provide a simple, purely memory-based character device driver to help developers understand the basic structure and functionality of character device drivers.
1. Overview of SCULL Character Device Driver Design
The SCULL device driver simulates a character device, with its operations performed in main memory and without physical hardware. Common features of the SCULL driver include:
2. Basic Design Structure of SCULL Program
The design of the SCULL driver generally includes the following components:
3. Code Example of SCULL Driver
1. Define Device Data Structure
SCULL usescircular buffers to store data. Each device corresponds to a buffer.
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define SCULL_SIZE 1024 // Define buffer size
#define SCULL_MAJOR 0 // Dynamically allocate major device number
#define DEVICE_NAME "scull"// SCULL device data structure
struct scull_dev {
char *data; // Data buffer
size_t size; // Current data size
struct cdev cdev; // Character device structure
};
struct scull_dev *scull_device; // SCULL device instance
dev_t dev_no; // Device number
2. Implementation of File Operation Functions
<span>open()</span> function
Initialization checks are performed when the device is opened.
int scull_open(struct inode *inode, struct file *filp) {
struct scull_dev *dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev;
pr_info("SCULL device opened\n");
return 0;
}
<span>release()</span> function
Resources are released when the device is closed.
int scull_release(struct inode *inode, struct file *filp) {
pr_info("SCULL device released\n");
return 0;
}
<span>read()</span> function
Reads data from the device to user space.
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
struct scull_dev *dev = filp->private_data;
size_t available = dev->size - *f_pos;
size_t to_read = min(count, available);
if (to_read == 0) return 0; // No data to read
if (copy_to_user(buf, dev->data + *f_pos, to_read)) {
return -EFAULT;
}
*f_pos += to_read;
pr_info("SCULL read %zu bytes\n", to_read);
return to_read;
}
<span>write()</span> function
Writes data from user space to the device.
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
struct scull_dev *dev = filp->private_data;
if (*f_pos + count > SCULL_SIZE)
count = SCULL_SIZE - *f_pos;
if (count == 0) return -ENOSPC;
if (copy_from_user(dev->data + *f_pos, buf, count)) {
return -EFAULT;
}
*f_pos += count;
dev->size = max(dev->size, *f_pos);
pr_info("SCULL wrote %zu bytes\n", count);
return count;
}
File Operation Structure
The above functions are registered into the<span>file_operations</span> structure.
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.open = scull_open,
.release = scull_release,
.read = scull_read,
.write = scull_write,
};
3. Character Device Registration and Initialization
Initialize the device when the module is loaded
static int __init scull_init(void) {
int result;
// Allocate device number
result = alloc_chrdev_region(&dev_no, 0, 1, DEVICE_NAME);
if (result < 0) {
pr_err("Failed to allocate device number\n");
return result;
}
// Allocate device structure
scull_device = kmalloc(sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_device) {
unregister_chrdev_region(dev_no, 1);
return -ENOMEM;
}
memset(scull_device, 0, sizeof(struct scull_dev));
// Allocate data buffer
scull_device->data = kmalloc(SCULL_SIZE, GFP_KERNEL);
if (!scull_device->data) {
kfree(scull_device);
unregister_chrdev_region(dev_no, 1);
return -ENOMEM;
}
// Initialize character device
cdev_init(&scull_device->cdev, &scull_fops);
scull_device->cdev.owner = THIS_MODULE;
result = cdev_add(&scull_device->cdev, dev_no, 1);
if (result) {
pr_err("Failed to add cdev\n");
kfree(scull_device->data);
kfree(scull_device);
unregister_chrdev_region(dev_no, 1);
return result;
}
pr_info("SCULL device initialized with major %d\n", MAJOR(dev_no));
return 0;
}
Clean up resources when the module is unloaded
static void __exit scull_exit(void) {
cdev_del(&scull_device->cdev);
kfree(scull_device->data);
kfree(scull_device);
unregister_chrdev_region(dev_no, 1);
pr_info("SCULL device unregistered\n");
}module_init(scull_init);
module_exit(scull_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple SCULL Character Device");
The SCULL driver simulates a character device through memory buffers, focusing on:
- Implementation of file operation interfaces (
<span>read</span>,<span>write</span>,<span>open</span>,<span>release</span>). - Device number allocation and registration.
- Character device initialization and resource management.
Through the SCULL example, you can familiarize yourself with the overall design and development process of Linux character device drivers.