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

相关推荐
昵称p7 小时前
杂项驱动开发
驱动开发·gpio子系统·杂项驱动开发
7yewh12 小时前
嵌入式硬件杂谈(四)-高速板PCB设计 高速信号全面讲解 蛇形线 等长线 差分对 阻抗对
驱动开发·嵌入式硬件·mcu·物联网·硬件工程·pcb工艺·精益工程
lishing61 天前
Linux驱动开发(9):pinctrl子系统和gpio子系统--led实验
linux·运维·驱动开发
lishing61 天前
Linux驱动开发(7):使用设备树实现RGB 灯驱动
linux·驱动开发
TeYiToKu1 天前
笔记整理—linux驱动开发部分(13)块设备
linux·c语言·驱动开发·笔记·嵌入式硬件·arm
网易独家音乐人Mike Zhou1 天前
【Linux驱动开发】irq中断配置API及中断应用 阻塞休眠和非阻塞的驱动操作
linux·c语言·驱动开发·stm32·单片机·mcu·iot
believe、悠闲1 天前
GetVolumeInformation函数使用记录
c++·windows·驱动开发
不怕犯错,就怕不做1 天前
修复kernel编译栈帧大小异常问题error: the frame size of 1928 bytes is larger than 1024 bytes
linux·arm开发·驱动开发
bigbig猩猩1 天前
Gin 框架中的表单处理与数据绑定
驱动开发·gin