文章目录
符设备驱动架构
如上图所示,应用层程序直接用系统提供的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函数中后面的两个参数我们一直都没用到,应该是有特殊的需求的时候才需要用到。