字符设备驱动02 | 学习笔记

简介: 快速学习字符设备驱动02

开发者学堂课程【物联网开发 - Linux驱动开发实操演练:字符设备驱动02】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/657/detail/10874


字符设备驱动02


内容介绍:

一、Cdev 结构体

二、常用的驱动接口


一、Cdev 结构体

提到了一个描述所有字符。字符设备驱动的结构体。这个结构体叫 CDeV。结构体CDeV 什么意思呢?

C 就是一个 char 的意思,就是字符,然后 dev 是 device,就是 CdeV 结合起来就是一个字符设备,这个设备,这是内核,给它起个名字叫 cdev 结构体。

看一下这个 CDeV 结构体里到底有哪些成员变量?

描述所有字符设备驱动的结构体:

Cdev 结构体:

struct cdev {

struct kobject kobj;

struct module *owner;

const struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count:

};

看到的这个成员变量,实际上不是很多,有几个比较重要的成员变量,给大家详细进行分析一下。

内核设备用来描述字符设备锁通用的结构体。

Kobj:关于设备模型的相关内容

Owner: 关于设备模型的相关内容,赋值 THIS_MODILE(模块)

再往下,这是一个 count,是设备计数。或者设备个数。

也就是驱动,一般情况下是一个驱动,对应的是一个驱动的一个设备,但是有的时候,实际上一个驱动的是可以驱动同类型的多个设备的。

那比如说一个驱动驱动的,同一个同一类设备驱动了三个,那时候这个 count,实际上就可以写个三是,也就是驱动了对应的三个同类型的设备. 看完 CdeV 结构体里面的成员变量以后,就知道了,这里重点,分析的第一就是设备号,第二个,就是这个结构体

operation 指针:

File_operations * 结构体指针:

image.png

除了 owner 之外全部都为函数指针

称此结构体为操作方法集(提供给应用层)。通过函数指针内核来调用设备,有这么一个结构体指针,而且,里面都是一些函数指针,有什么用呢?

这个 Linux 内核运行起来以后,应用程序想操作设备的时候,是不是必须得通过系统调用,然后,调到驱动的方法,那怎样才能调到驱动的方法,就是通过这个函数指针的方式去调用的,具体的设备驱动,具体的写驱动的工程师的工程师来完成,那在写的时候,不同的驱动,它的这个操作方法其实是不一样的,因此只需要把函数完成以后,把函数名复制函数赋值给这个函数指针以后,就能调到这个设备驱动,调用这个操作方法了。

image.png


二、常用的驱动接口

struct file_operations {

struct module *owner;

loff_t (*llseek)(struct file *loff_t,int);

ssize_t (*read)(struct file *char user *size t,loff_t );

ssize_t (*write)(struct file *const char user *size t,loff_t );

unsignedIint (*poll)(struct_file *struct poll_table_struct );

long (*unlocked_ioctl)(struct file *unsigned int,unsigned long);

int (*mmap)(struct file *struct vm_area_struct );

Int (*open)(struct inode *struct file *);

int (*release)(struct inode *struct file *);

int(*fasync)(int,struct file *int);

};

Read write:是调用驱动中的 read write 的方法。

Open: 底层最终调的是底层驱动 open 所对应的一个方法。

剩下的后面在介绍。

List_head 列表,内核在管理 CDeV 时,是通过一种列表的形式,去管理的所有的设备。

Dev_t: 设备号:

是唯一用来描述设备的一个编号,用来标识设备的。

数据类型:

代码:

21:typedef __n32__kernel dev t;

22:

23:typedef__kernel_fd_set    fd_set;

24:typedef__kernel_dev_t     dev_t;

25:typedef__kernel_ino_t      ino_t;

26:typedef__kernel_mode_t   mode_t;

27:typedef unsigned short    umode_t;

28:typedef__kernel_nlink_t    nlink_t;

32位无符号整型:主数据号+次设备号‘

这个设备跟这个设备号是一一对应的,一个驱动,可以驱动同类型的多个设备,那驱动同类型的多个设备的时候,主设备号去描述的是一类设备,然后次设备号,就是不同的设备,比如同样都是一个这个usb设备驱动,这个主设备号就是相同的,次设备号,比如一设备对应的是一,二设备对应的是二,三设备对应的是三。

内核提供了两个宏:

代码:

MAJOR(dev_t dev)         //从设备号中提取主设备号

MINOR(dev_t dev)           //从设备号中提取次设备号

MKDEV(int ma,int mi)  //根据主次设备号生成设备号

有了此三个宏,设备号的操作就可以完成了。

设备号的提取和生成如何实现:

代码:

#define MAJOR(dey) ((unsigned int) ((dey) >> MINORBITS))

#define MINOR(dev)   ((unsignedint) ((dey) & MINORMASK))

#define MKDEV(ma,mi)  (((ma) << MINORBITS) | (mi))

#define print_dov_t(buffer,dev)

sprint((buffer3“%u:%u\n“,MAJOR(dev), MINOR(dev))

#define format_dev_t(buffer,dev)

({

sprintf(uffer,”%u:%u”,MJoR(dev),MAJOR(dov);

buffer;

})

为宏函数。

将设备号右移20位,然后强转成一个无符号整型。

得到的是高12位。

所以主设备号是由高设备号组成。

代码:

#define MAJOR(dey) ((unsigned int) ((dey) >> MINORBITS))

#define MINOR(dev)   ((unsignedint) ((dey) & MINORMASK))

#define MKDEV(ma,mi)  (((ma) << MINORBITS) | (mi))

#define print_dov_t(buffer,dev)

sprint((buffer3“%u:%u\n“,MAJOR(dev), MINOR(dev))

#define MINOR(dev)   ((unsigned int)  ((dev)&((1U<<20)-1)))

一左移20位减一,想一下一左移二十位,后面有20个协议,就是说前面全是零,后面的20位全要。

得到的是低20位。

设备号:高12位的主设备号和第12位的次设备号组成。

Count:设备计数(个数)一个驱动可驱动同一个或者多类设备。

相关文章
|
5月前
|
Linux 程序员 芯片
【Linux驱动】普通字符设备驱动程序框架
【Linux驱动】普通字符设备驱动程序框架
|
安全 网络协议 Linux
Linux驱动开发 设备驱动的基本概念
Linux驱动开发 设备驱动的基本概念
|
6月前
|
Linux API
字符设备驱动(1):Linux字符设备驱动结构
字符设备驱动(1):Linux字符设备驱动结构
94 1
|
存储 Linux API
嵌入式Linux 字符设备驱动标准ioctl接口
嵌入式Linux 字符设备驱动标准ioctl接口
|
存储 安全 Unix
【Linux驱动】字符设备驱动
【Linux驱动】字符设备驱动
62 0
|
算法 Linux C语言
Linux驱动中常用的一些接口函数
Linux驱动中常用的一些接口函数
|
Linux 编译器
Linux设备驱动---字符设备驱动接口函数
Linux设备驱动---字符设备驱动接口函数
175 1
|
存储 Linux 文件存储
Linux驱动入门(6.1)LED驱动---设备树
Linux驱动入门(6.1)LED驱动---设备树
188 0
|
存储 缓存 Unix
Linux设备驱动程序(三)——字符驱动
本章的目的是编写一个完整的字符设备驱动,我们开发一个字符驱动是因为这一类适合大部分简单硬件设备,字符驱动也比块驱动易于理解。
246 0
|
Linux API
字符设备驱动基础知识
字符设备驱动基础知识