目录
BSP 设备驱动开发的选择驱动模型:Linux 字符设备驱动模型
BSP 设备驱动开发的选择驱动模型:Linux 字符设备驱动模型
在嵌入式系统开发中,设备驱动是连接硬件和操作系统的桥梁,负责管理和控制硬件设备。Linux 操作系统提供了多种设备驱动模型,其中字符设备驱动模型是最常见的一种。以下是详细的介绍和步骤,帮助你选择和开发 Linux 字符设备驱动。
1. 字符设备驱动模型概述
1.1 特点
- 顺序访问:字符设备通常支持顺序访问,每次读写操作的数据量较小。
- 非缓冲:字符设备通常是非缓冲的,即数据直接从设备读取或写入设备。
- 多用户支持:字符设备可以支持多个用户同时访问。
1.2 常见应用
- 串口:UART、RS-232 等。
- 键盘和鼠标:输入设备。
- GPIO:通用输入输出端口。
- I2C 和 SPI 设备:传感器、EEPROM 等。
- 音频设备:声卡、麦克风等。
2. 开发环境准备
2.1 获取 Linux 内核源码
从 Linux 内核官方网站或 GitHub 仓库获取最新的内核源码:
sh
深色版本
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
2.2 安装开发工具
确保安装了必要的开发工具和库。例如,在 Ubuntu 上可以使用以下命令安装:
sh
深色版本
sudo apt-get update
sudo apt-get install build-essential kernel-package libncurses5-dev libssl-dev
3. 创建字符设备驱动
3.1 注册字符设备
在内核模块中注册字符设备,分配设备号和设备类。
3.1.1 定义设备号
c
深色版本
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "my_char_dev"
#define CLASS_NAME "my_class"
static dev_t dev_num;
static struct cdev my_cdev;
static struct class *my_class;
static struct device *my_device;
static int major = 0;
static int minor = 0;
static int my_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "my_char_dev: device opened\n");
return 0;
}
static int my_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "my_char_dev: device closed\n");
return 0;
}
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
printk(KERN_INFO "my_char_dev: read %zu bytes from device\n", count);
return 0;
}
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
printk(KERN_INFO "my_char_dev: wrote %zu bytes to device\n", count);
return count;
}
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
static int __init my_init(void) {
int ret;
// 动态分配设备号
ret = alloc_chrdev_region(&dev_num, minor, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ALERT "Failed to register device\n");
return ret;
}
// 初始化字符设备
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;
ret = cdev_add(&my_cdev, dev_num, 1);
if (ret < 0) {
unregister_chrdev_region(dev_num, 1);
printk(KERN_ALERT "Failed to add character device\n");
return ret;
}
// 创建设备类
my_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(my_class)) {
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
printk(KERN_ALERT "Failed to create device class\n");
return PTR_ERR(my_class);
}
// 创建设备文件
my_device = device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME);
if (IS_ERR(my_device)) {
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
printk(KERN_ALERT "Failed to create device file\n");
return PTR_ERR(my_device);
}
printk(KERN_INFO "my_char_dev: device registered successfully\n");
return 0;
}
static void __exit my_exit(void) {
device_destroy(my_class, dev_num);
class_destroy(my_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
printk(KERN_INFO "my_char_dev: device unregistered\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device driver");
3.2 编译内核模块
创建一个 Makefile 文件,用于编译内核模块:
makefile
深色版本
obj-m += my_char_dev.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
编译内核模块:
sh
深色版本
make
3.3 加载和卸载内核模块
加载内核模块:
sh
深色版本
sudo insmod my_char_dev.ko
卸载内核模块:
sh
深色版本
sudo rmmod my_char_dev
4. 测试字符设备驱动
4.1 创建设备节点
如果设备节点不存在,可以手动创建:
sh
深色版本
sudo mknod /dev/my_char_dev c $(cat /proc/devices | grep my_char_dev | awk '{print $1}') 0
4.2 测试读写操作
使用 cat
和 echo
命令测试设备节点的读写操作:
sh
深色版本
echo "Hello, World!" > /dev/my_char_dev
cat /dev/my_char_dev
5. 总结
通过上述步骤,可以系统地进行 Linux 字符设备驱动的开发和测试。字符设备驱动模型适用于多种硬件设备,能够提供灵活的设备管理和控制功能。希望这些详细的解释和示例能够帮助你更好地理解和实施字符设备驱动的开发。