当前位置: 首页 > news >正文

嵌入式Linux字符设备驱动开发

嵌入式Linux字符设备驱动开发

一、字符设备驱动基础概念

1. 字符设备定义

字符设备是Linux驱动开发中最基本的一类设备驱动,以字节流形式进行读写操作,读写数据是分先后顺序的。常见字符设备包括:

  • 点灯
  • 按键
  • IIC
  • SPI
  • LCD等

2. 应用程序与驱动调用关系

Linux应用程序通过标准API调用驱动程序,流程如下:

open()、close()、read()、write()等API
↓
C库函数
↓
系统调用进入内核
↓
内核驱动程序中的open()、close()、read()、release()等函数
↓
具体硬件设备

3. file_operations结构体

这是Linux内核驱动操作函数集合,定义在include/linux/fs.h中,包含以下重要函数:

函数描述
owner指向拥有该结构体的模块
llseek修改文件当前读写位置
read读取设备文件
write向设备文件写入数据
poll查询设备是否可进行非阻塞读写
unlocked_ioctl设备控制功能
mmap将设备内存映射到进程空间
open打开设备文件
release关闭设备文件

二、字符设备驱动开发步骤

1. 驱动模块加载与卸载

Linux驱动可编译进内核或编译为模块(.ko)。模块开发更便于调试,只需加载/卸载模块即可。

static int __init xxx_init(void) {// 初始化代码return 0;
}static void __exit xxx_exit(void) {// 清理代码
}module_init(xxx_init);
module_exit(xxx_exit);

2. 字符设备注册与注销

使用register_chrdevunregister_chrdev函数注册和注销字符设备:

static int __init chrdevbase_init(void) {int retvalue = 0;retvalue = register_chrdev(CHARDEVBASE_MAJOR, CHARDEVBASE_NAME, &chrdevbase_fops);if(retvalue < 0) {// 错误处理}return 0;
}static void __exit chrdevbase_exit(void) {unregister_chrdev(CHARDEVBASE_MAJOR, CHARDEVBASE_NAME);
}

3. 设备操作函数实现

实现file_operations结构体中的具体操作函数:

static int chardevbase_open(struct inode *inode, struct file *file) {return 0;
}static int chardevbase_release(struct inode *inode, struct file *file) {return 0;
}static ssize_t chardevbase_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {// 实现读操作return 0;
}static ssize_t chardevbase_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {// 实现写操作return count;
}

4. LICENSE和作者信息添加

必须添加模块许可证信息,否则编译会报错:

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");

三、设备号管理

1. 设备号组成

设备号由主设备号和次设备号组成:

  • 主设备号:表示具体驱动,高12位
  • 次设备号:表示使用该驱动的各个设备,低20位

2. 设备号分配方法

  • 静态分配:直接指定设备号
  • 动态分配:使用alloc_chrdev_region自动分配

四、chrdevbase字符设备驱动开发实验

1. 驱动实现

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>#define CHARDEVBASE_MAJOR 200
#define CHARDEVBASE_NAME "chardevbase"char readbuf[100];  // 读缓冲区
char writebuf[100]; // 写缓冲区
char kerneldata[] = {"kernel data"}; // 内核数据// 打开设备
static int chardevbase_open(struct inode *inode, struct file *file) {return 0;
}// 关闭设备
static int chardevbase_release(struct inode *inode, struct file *file) {return 0;
}// 读取设备
static ssize_t chardevbase_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {int ret = 0;memcpy(readbuf, kerneldata, sizeof(kerneldata));ret = copy_to_user(buf, readbuf, count);if (ret < 0) {printk(KERN_ALERT "Failed to copy data to user space\n");return -EFAULT;}return ret;
}// 写入设备
static ssize_t chardevbase_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {int ret = 0;ret = copy_from_user(writebuf, buf, count);if (ret < 0) {printk(KERN_ALERT "Failed to copy data from user space\n");return -EFAULT;} else {writebuf[count] = '\0';printk(KERN_INFO "Kernel received from user: %s\n", writebuf);}return count;
}// 设备操作函数集合
static struct file_operations chardevbase_fops = {.owner = THIS_MODULE,.open = chardevbase_open,.release = chardevbase_release,.read = chardevbase_read,.write = chardevbase_write,
};// 驱动入口
static int __init chardevbase_init(void) {int ret = 0;printk(KERN_INFO "Character Device Base Module Initialized\n");ret = register_chrdev(CHARDEVBASE_MAJOR, CHARDEVBASE_NAME, &chardevbase_fops);if (ret < 0) {printk(KERN_ALERT "Failed to register character device\n");return ret;}return 0;
}// 驱动出口
static void __exit chardevbase_exit(void) {printk(KERN_INFO "Character Device Base Module Exited\n");unregister_chrdev(CHARDEVBASE_MAJOR, CHARDEVBASE_NAME);
}module_init(chardevbase_init);
module_exit(chardevbase_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");

2. 测试应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[]) {if (argc < 3) {fprintf(stderr, "Usage: %s <device_file>\n", argv[0]);return -1;}int fd = 0;char *filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {perror("Failed to open character device");return -1;}int ret = 0;if (atoi(argv[2]) == 1) { // 读操作char readbuf[100];ret = read(fd, readbuf, sizeof(readbuf));if (ret < 0) {perror("Failed to read from character device");close(fd);return -1;} else {printf("User read from character device: %s\r\n", readbuf);}} else if(atoi(argv[2]) == 2) { // 写操作char writebuf[100] = "Hello from user write buffer!";ret = write(fd, writebuf, strlen(writebuf));if (ret < 0) {perror("Failed to write to character device");close(fd);return -1;}}ret = close(fd);  // 关闭设备if (ret < 0) {perror("Failed to close character device");return -1;}return 0;
}

3. Makefile

KERNERDIR := /home/ubuntu2004/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENTDIR := $(shell pwd)obj-m := chardevbase.obuild : kernel_moduleskernel_modules:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) modulesclean:$(MAKE) -C $(KERNERDIR) M=$(CURRENTDIR) clean

五、驱动测试步骤

  1. 编译驱动模块

    make
    
  2. 加载驱动模块

    insmod chardevbase.ko
    # 或
    modprobe chardevbase.ko
    
  3. 创建设备节点文件

    mknod /dev/chardevbase c 200 0
    
  4. 测试读操作

    ./chardevbaseAPP /dev/chardevbase 1
    
  5. 测试写操作

    ./chardevbaseAPP /dev/chardevbase 2
    
  6. 卸载驱动模块

    rmmod chardevbase.ko
    

六、关键知识点总结

  1. 内核空间与用户空间交互

    • 使用copy_to_user()将数据从内核空间复制到用户空间
    • 使用copy_from_user()将数据从用户空间复制到内核空间
  2. printk与printf区别

    • printk用于内核空间输出
    • printf用于用户空间输出
    • printk支持消息级别分类
  3. 设备文件操作

    • 应用程序通过/dev目录下的设备文件操作硬件
    • 设备文件通过mknod命令创建
  4. 模块调试技巧

    • 使用insmodmodprobe加载模块
    • 使用rmmod卸载模块
    • 使用dmesg查看内核日志
  5. 设备号管理

    • 使用cat /proc/devices查看已使用设备号
    • 推荐使用动态分配设备号

gitee源码仓库

http://www.dtcms.com/a/357404.html

相关文章:

  • LFI-labs靶场通关教程
  • 串口通信1.0(串行并行)
  • 解决多种类潮湿敏感元器件的多温度、多时长的排潮烘干
  • 订餐后台项目-day02数据库模型定义笔记
  • DAY16-新世纪DL(DeepLearning/深度学习)战士:Q(机器学习策略)1
  • Go语言入门(13)-map
  • 科学融智学引领人机协同教育新范式
  • 吴恩达机器学习作业七:方差与偏差
  • 【上位机数据转换】数据结构原理及大小端
  • 《WINDOWS 环境下32位汇编语言程序设计》第8章 通用对话框
  • ssh端口转发的几种常用使用方式【本地端口转发、远程端口转发、反向端口转发、动态端口转发】
  • Jenkins 全方位指南:安装、配置、部署与实战应用(含图解)
  • Two-Twer模型做歌曲智能推荐与规则算法对比的优缺点分析
  • 对比rerank模型和embedding模型
  • 订餐后台管理系统 - day04退出登录与账号管理模块
  • C#简单组态软件开发
  • AlexNet:点燃深度学习革命的「卷积神经网络之王」
  • 50etf期权与现货套利是什么意思?
  • position属性
  • Linux学习:线程控制
  • FastAPI 入门科普:下一代高性能 Python Web 框架
  • 一般纳税人
  • 上海市赛/磐石行动2025决赛awd web2-python 4个漏洞详解
  • 漫谈《数字图像处理》之浅析图割分割
  • Java IO 流-详解
  • @GitLab 介绍部署使用详细指南
  • [Godot] C#获取MenuButton节点索引
  • 回车换行、缓冲区刷新、倒计时小程序
  • Woody:开源Java应用性能诊断分析工具
  • 智慧工地源码