嵌入式驱动开发详解1(系统调用)

文章目录

符设备驱动架构

如上图所示,应用层程序直接用系统提供的API函数即可调用驱动层相应的函数,中间的具体过程都是由linux内核实现的,下面我们用read函数来举例子。

read函数详解

用户层read函数

c 复制代码
ssize_t read(int fd, void *buf, size_t count)

read 函数参数含义如下:
fd :要读取的文件描述符,读取文件之前要先用 open 函数打开文件,open 函数打开文件成 功以后会得到文件描述符。
buf :数据读取到此 buf 中。
count :要读取的数据长度,也就是字节数。
返回值 :读取成功的话返回读取到的字节数;如果返回 0 表示读取到了文件末尾;如果返 回负值,表示读取失败。
在 Ubuntu 中输入"man 2 read"命令即可查看 read 函数的详细内容。

内核层read函数

c 复制代码
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

返回类型 ssize_t :ssize_t 是一个数据类型,用于表示有符号的大小或者返回值。在这种情况下,它表示读取操作成功读取的字节数,或者在发生错误时返回的错误码。
struct file *:指向一个file结构体的指针,该结构体包含了与文件描述符相关的信息,如文件的当前位置(f_pos)、文件状态标志(f_flags)等。
char _user *:这是一个指向用户空间缓冲区的指针,用于存储从设备读取的数据。__user 关键字表示这个指针指向用户空间的内存区域,内核需要通过特定的接口来访问。
size_t :一个无符号整数类型,表示尝试读取的最大字节数。
loff_t *:指向一个loff_t类型的指针,loff_t是一个数据类型,用于表示文件偏移量。这个参数用于指定从文件的哪个位置开始读取数据,并在读取操作后更新文件的当前位置。
这个函数指针通常作为file_operations结构体的一部分,当用户空间发起一个read系统调用时,内核会根据文件描述符找到对应的file结构体,并调用其read函数指针指向的函数来执行实际的读操作。

具体实现

用户层代码

用户层的代码如下所示:

c 复制代码
int main(int argc,char *argv[])
{
    int fd,ret;
    char *filename;
    unsigned char key_value;
    filename = argv[1];
    fd = open(filename ,O_RDWR);
    while(1){
        ret = read(fd,&key_value,sizeof(key_value)); 
        if(ret > 0){
        	if(key_value == KEY_VALUE){
            	printf("KEY PRESS, value = %x\r\n",key_value);
        	}
        }
    }
    ret = close(fd);
    return 0;
}

只需要重点关注read函数的实现即可。

内核层代码

内核层代码的实现如下:

c 复制代码
static ssize_t key_read(struct file *file, char __user *buf,
				size_t count, loff_t *off)
{
	unsigned char value;
	int ret;
	struct key_dev *dev = file->private_data;
	if(gpio_get_value(dev->key_gpio) == 0){
		while(!gpio_get_value(dev->key_gpio));
		atomic_set(&dev->keyvalue,KEY_VALUE);
	}else {
		atomic_set(&dev->keyvalue,INVAKEY);
	}
	value = atomic_read(&dev->keyvalue);
	ret = copy_to_user(buf,&value,sizeof(value));
	return ret;
}
static const struct file_operations key_fops = {
	.owner	= THIS_MODULE,
	.open	= key_open,
	.read	= key_read,
};

只需要关注read函数的返回值跟copy_to_user函数的执行即可。

细节分析

在上面的代码中,用户层主要获取了两个数据,一个是返回值ret,一个是保存在buf中的数据key_value,ret是内涵层read函数return返回的值,而key_value是我们内核层read函数里面的copy_to_user返回的值。

c 复制代码
copy_to_user(buf,&value,sizeof(value));

可以出buf对应的就是内核层read函数里的" char __user *buf ",read函数中后面的两个参数我们一直都没用到,应该是有特殊的需求的时候才需要用到。

相关推荐
cxr8283 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot3 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶4 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot4 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday5 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8287 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday7 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发