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

相关推荐
努力自学的小夏2 小时前
RK3568 Linux驱动学习——Linux驱动开发准备工作
linux·驱动开发·笔记·学习
wifi chicken3 小时前
Linux Wlan 无线网络驱动开发-scan协议全流程详解
linux·驱动开发·wifi协议·驱动学习
通信小小昕9 小时前
Petalinux驱动开发
驱动开发
mmoyula19 小时前
【RK3568 PWM 子系统(SG90)驱动开发详解】
android·linux·驱动开发
风吹花中花吹风1 天前
原创-锐能微82xx系列电能计量芯片软件驱动开发与精度校准流程完全指南
驱动开发·单片机·嵌入式硬件·锐能微·计量芯片·精度校准·电能表校准
努力做小白3 天前
Linux驱动19 --- FFMPEG
linux·运维·驱动开发·单片机·嵌入式硬件·ffmpeg
我不是程序猿儿3 天前
【Servo】裸机还是RTOS驱动架构如何选?
驱动开发·fpga开发·架构·伺服驱动器·伺服
努力做小白3 天前
Linux驱动18 --- LCD 屏
linux·驱动开发·单片机·嵌入式硬件·lvgl·屏幕
thinkMoreAndDoMore4 天前
linux驱动开发(21)-Linux设备驱动模型(一)
linux·驱动开发
sukalot5 天前
window显示驱动开发—以追溯方式要求自由线程 CalcPrivate DDI
驱动开发