1. 介绍
设备驱动程序是内核模块,用于实现硬件设备和操作系统之间的通信。它们充当硬件和操作系统之间的桥梁,使应用程序能够与硬件交互,而无需了解硬件实现的细节。
设备驱动程序中的关键概念
- 内核模块架构: 设备驱动程序通常作为可加载的内核模块实现。
- 设备类型: 设备可以分为字符设备、块设备和网络设备。
- 驱动程序生命周期: 包括初始化、操作和清理阶段。
- 硬件接口: 驱动程序通过内存映射I/O、中断和DMA与硬件交互。
2. 字符设备实现
字符设备作为字节流访问,类似于文件。下面是C语言实现的基本字符设备驱动程序:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "chardev"
#define CLASS_NAME "char_class"
static struct class *char_class;
static struct cdev char_cdev;
static dev_t dev_num;
static int char_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "Character device opened\n");
return 0;
}
static int char_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "Character device closed\n");
return 0;
}
static ssize_t char_read(struct file *file,
char __user *buf,
size_t count,
loff_t *offset) {
char data[] = "Hello from kernel\n";
size_t datalen = strlen(data);
if (*offset >= datalen)
return 0;
if (count > datalen - *offset)
count = datalen - *offset;
if (copy_to_user(buf, data + *offset, count))
return -EFAULT;
*offset += count;
return count;
}
static ssize_t char_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *offset) {
char kernel_buf[1024];
if (count > sizeof(kernel_buf))
return -EINVAL;
if (copy_from_user(kernel_buf, buf, count))
return -EFAULT;
printk(KERN_INFO "Received: %.*s\n", (int)count, kernel_buf);
return count;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = char_open,
.release = char_release,
.read = char_read,
.write = char_write
};
static int __init char_driver_init(void) {
int ret;
// Allocate device number
ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ALERT "Failed to allocate device number\n");
return ret;
}
// Create device class
char_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(char_class)) {
unregister_chrdev_region(dev_num, 1);
return PTR_ERR(char_class);
}
// Initialize and add character device
cdev_init(&char_cdev, &fops);
ret = cdev_add(&char_cdev, dev_num, 1);
if (ret < 0) {
class_destroy(char_class);
unregister_chrdev_region(dev_num, 1);
return ret;
}
// Create device file
if (device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME) == NULL) {
cdev_del(&char_cdev);
class_destroy(char_class);
unregister_chrdev_region(dev_num, 1);
return -EFAULT;
}
printk(KERN_INFO "Character device driver loaded\n");
return 0;
}
static void __exit char_driver_exit(void) {
device_destroy(char_class, dev_num);
cdev_del(&char_cdev);
class_destroy(char_class);
unregister_chrdev_region(dev_num, 1);
printk(KERN_INFO "Character device driver unloaded\n");
}
module_init(char_driver_init);
module_exit(char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver");
3. 设备注册和操作
设备注册涉及创建和管理设备上下文,这些上下文存储与设备相关的状态和资源。
设备上下文实现
以下是设备注册和管理的实现:
struct device_context {
struct mutex lock;
void __iomem *base_addr;
int irq;
struct work_struct work;
wait_queue_head_t wait_queue;
bool data_available;
};
static struct device_context *dev_ctx;
static long device_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg) {
struct device_context *ctx = file->private_data;
int ret = 0;
mutex_lock(&ctx->lock);
switch (cmd) {
case DEVICE_RESET:
ret = reset_device(ctx);
break;
case DEVICE_GET_STATUS:
ret = get_device_status(ctx, (void __user *)arg);
break;
case DEVICE_SET_CONFIG:
ret = set_device_config(ctx, (void __user *)arg);
break;
default:
ret = -ENOTTY;
}
mutex_unlock(&ctx->lock);
return ret;
}
4. I/O 控制机制
I/O控制(ioctl)允许用户空间应用程序向设备驱动程序发送命令。以下是设备I/O控制的实现:
#define DEVICE_IOC_MAGIC 'k'
#define DEVICE_RESET _IO(DEVICE_IOC_MAGIC, 0)
#define DEVICE_GET_STATUS _IOR(DEVICE_IOC_MAGIC, 1, struct device_status)
#define DEVICE_SET_CONFIG _IOW(DEVICE_IOC_MAGIC, 2, struct device_config)
struct device_status {
uint32_t state;
uint32_t errors;
uint64_t bytes_processed;
};
struct device_config {
uint32_t mode;
uint32_t timeout;
uint32_t buffer_size;
};
static int reset_device(struct device_context *ctx) {
void __iomem *base = ctx->base_addr;
// Reset hardware registers
writel(DEVICE_RESET_CMD, base + DEVICE_CTRL_REG);
// Wait for reset completion
if (!wait_for_completion_timeout(&ctx->reset_complete,
msecs_to_jiffies(1000)))
return -ETIMEDOUT;
return 0;
}
5. 中断处理
中断处理对于及时响应硬件事件至关重要。以下是中断处理的一个实现:
static irqreturn_t device_isr(int irq, void *dev_id) {
struct device_context *ctx = dev_id;
uint32_t status;
// Read interrupt status
status = readl(ctx->base_addr + DEVICE_INT_STATUS_REG);
if (!(status & DEVICE_INT_MASK))
return IRQ_NONE;
// Clear interrupt
writel(status, ctx->base_addr + DEVICE_INT_CLEAR_REG);
// Schedule bottom half
schedule_work(&ctx->work);
return IRQ_HANDLED;
}
static void device_work_handler(struct work_struct *work) {
struct device_context *ctx =
container_of(work, struct device_context, work);
// Process interrupt data
process_device_data(ctx);
// Wake up waiting processes
ctx->data_available = true;
wake_up_interruptible(&ctx->wait_queue);
}
- 中断服务例程 (ISR): device_isr() 函数通过读取中断状态并调度工作队列来处理硬件中断。
- 工作队列处理器: device_work_handler() 函数处理中断数据并唤醒等待进程。
6. 内存管理
设备驱动程序中的内存管理涉及分配和管理DMA缓冲区以及映射设备寄存器。
DMA缓冲区实现
以下是DMA缓冲区管理的实现:
struct dma_buffer {
void *cpu_addr;
dma_addr_t dma_addr;
size_t size;
};
static int allocate_dma_buffer(struct device *dev,
struct dma_buffer *buf,
size_t size) {
buf->size = size;
buf->cpu_addr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL);
if (!buf->cpu_addr)
return -ENOMEM;
return 0;
}
static void free_dma_buffer(struct device *dev, struct dma_buffer *buf) {
if (buf->cpu_addr) {
dma_free_coherent(dev, buf->size, buf->cpu_addr, buf->dma_addr);
buf->cpu_addr = NULL;
}
}
static int setup_device_memory(struct device_context *ctx) {
int ret;
// Map device registers
ctx->base_addr = ioremap(DEVICE_BASE_ADDR, DEVICE_REG_SIZE);
if (!ctx->base_addr)
return -ENOMEM;
// Allocate DMA buffers
ret = allocate_dma_buffer(ctx->dev, &ctx->rx_buffer, RX_BUFFER_SIZE);
if (ret)
goto err_unmap;
ret = allocate_dma_buffer(ctx->dev, &ctx->tx_buffer, TX_BUFFER_SIZE);
if (ret)
goto err_free_rx;
return 0;
err_free_rx:
free_dma_buffer(ctx->dev, &ctx->rx_buffer);
err_unmap:
iounmap(ctx->base_addr);
return ret;
}
- DMA缓冲区结构: dma_buffer结构存储缓冲区的CPU和DMA地址。
- 缓冲区分配: allocate_dma_buffer()函数使用dma_alloc_coherent()分配一个DMA缓冲区。
- 缓冲区清理: free_dma_buffer()函数使用dma_free_coherent()释放一个DMA缓冲区。
7. 调试和测试
调试和测试对于确保设备驱动程序的可靠性至关重要。以下是调试设施的实现:
#define DRIVER_DEBUG 1
#if DRIVER_DEBUG
#define dev_dbg_reg(dev, reg, val) \
dev_dbg(dev, "Register %s = 0x%08x\n", #reg, val)
#else
#define dev_dbg_reg(dev, reg, val) do {} while (0)
#endif
static void dump_registers(struct device_context *ctx) {
uint32_t val;
val = readl(ctx->base_addr + DEVICE_CTRL_REG);
dev_dbg_reg(ctx->dev, DEVICE_CTRL_REG, val);
val = readl(ctx->base_addr + DEVICE_STATUS_REG);
dev_dbg_reg(ctx->dev, DEVICE_STATUS_REG, val);
val = readl(ctx->base_addr + DEVICE_INT_MASK_REG);
dev_dbg_reg(ctx->dev, DEVICE_INT_MASK_REG, val);
}
static int device_debugfs_init(struct device_context *ctx) {
ctx->debugfs_dir = debugfs_create_dir(DEVICE_NAME, NULL);
if (!ctx->debugfs_dir)
return -ENOMEM;
debugfs_create_file("registers", 0444, ctx->debugfs_dir,
ctx, &device_regs_fops);
return 0;
}
8. 错误处理
错误处理确保驱动程序能够从意外情况中恢复。以下是错误处理的实现:
static int handle_device_error(struct device_context *ctx, int error) {
dev_err(ctx->dev, "Device error: %d\n", error);
// Log error details
log_error_state(ctx);
// Attempt recovery
if (error == DEVICE_ERROR_TIMEOUT) {
dev_warn(ctx->dev, "Attempting device reset\n");
return reset_device(ctx);
}
if (error == DEVICE_ERROR_DMA) {
dev_warn(ctx->dev, "Resetting DMA engine\n");
return reset_dma(ctx);
}
// Critical error - disable device
disable_device_interrupts(ctx);
return -EIO;
}
static void log_error_state(struct device_context *ctx) {
struct device_state state;
// Capture device state
get_device_state(ctx, &state);
// Log to kernel ring buffer
dev_err(ctx->dev, "Error state captured:\n");
dev_err(ctx->dev, "Status: 0x%08x\n", state.status);
dev_err(ctx->dev, "Control: 0x%08x\n", state.control);
dev_err(ctx->dev, "Interrupt status: 0x%08x\n", state.int_status);
// Store in device context for debugging
memcpy(&ctx->last_error_state, &state, sizeof(state));
}
9. 结论
设备驱动程序开发需要对硬件接口和内核编程有深入的了解。正确实现字符设备、中断处理和内存管理对于可靠的驱动程序运行至关重要。