嵌入式驱动开发详解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函数中后面的两个参数我们一直都没用到,应该是有特殊的需求的时候才需要用到。

相关推荐
俺不是文盲1 小时前
Linux驱动开发:SPI设备树处理过程
linux·驱动开发
林政硕(Cohen0415)12 小时前
Linux驱动开发进阶(七)- DRM驱动程序设计
linux·驱动开发·drm
应以大橘为重20 小时前
interrupt子系统中的数据结构
linux·数据结构·驱动开发
jz_ddk2 天前
[实战] linux驱动框架与驱动开发实战
linux·运维·c语言·驱动开发·嵌入式硬件
俺不是文盲2 天前
Linux驱动开发:SPI驱动开发原理
linux·驱动开发
冬瓜3123 天前
物联网外设管理服务平台
linux·运维·驱动开发
程序员JerrySUN3 天前
驱动开发硬核特训 · Day 1
java·linux·运维·开发语言·c++·驱动开发
sukalot3 天前
Windows 图形显示驱动开发-WDDM 2.0功能 64KB页面支持
驱动开发
触角010100013 天前
STM32看门狗应用实战:独立看门狗与窗口看门狗深度解析(下) | 零基础入门STM32第九十五步
驱动开发·stm32·单片机·嵌入式硬件·物联网
华清远见IT开放实验室4 天前
【通知】STM32MP157驱动开发课程全新升级!零基础入门嵌入式Linux驱动,掌握底层开发核心技能!
linux·驱动开发·stm32·开发板·课程升级