欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Linux设备驱动分类、字符设备驱动、如何将cdev添加到内核?

程序员文章站 2022-12-04 09:00:45
1.Linux设备驱动分类 按管理的设备硬件来分 字符设备 按字节流访问,能够顺序访问,也能够指定位置访问 按键 串口 终端 触摸屏 LCD等 块设备 在Unix系统下...

1.Linux设备驱动分类

按管理的设备硬件来分

字符设备

按字节流访问,能够顺序访问,也能够指定位置访问

按键 串口 终端 触摸屏 LCD等

块设备

在Unix系统下,块设备按一定的数据块进行访问,数据块为512bytes 1K等

在Linux下,块设备既可以按数据块的方式访问,也可以按字节流访问, 他和字符设备的区别在于linux系统中描述块设备和字符设备的数据结构和操作方法是不一样的

网络设备

网卡,网络设备一般要结合tcp/ip协议栈来实现

2.字符设备驱动

驱动程序的作用:

1.管理对应的硬件

2.给用户提供访问硬件的操作的方法(接口)

应用程序如何访问硬件?

硬件设备在linux系统下,会以设备文件的形式存在,设备文件(字符和块)在/dev/

,那么应用程序要访问硬件其实就是访问对应的设备文件

应用程序如何访问设备文件?

通过调用系统调用来实现对其的访问,访问设备文件和访问普通文件的方式是一样的

open read write ioctl mmap close......

应用程序如何通过设备文件在非常多的驱动中找到对应的硬件驱动?

设备文件本身包含一些属性:

设备文件是字符设备文件(c)还是块设备文件(b)

设备文件还包括主设备号和次设备号这两个重要的属性

应用程序就是通过主设备号找到对应的驱动程序

一个驱动程序只有一个主设备号

次设备号的作用

次设备号用于找到具体要访问的设备个体

设备号:主设备号和次设备号

数据类型:

dev_t(unsigned int)

高12位:主设备号

低20位:次设备号

设备号操作宏

MAJOR

MINOR

MKDEV

设备号是属于系统资源,如果要实现驱动和设备号的绑定,首先必须向内核申请设备号资源,只有完成申请之后,才能和驱动程序绑定

如何向内核申请设备号?

静态分配和动态分配

静态分配

1>查看哪些主设备号已经被使用

cat /proc/devices

Character devices:字符设备

1 mem

5 /dev/tty

5 /dev/console

5 /dev/ptmx

10 misc

13 input

21 sg

29 fb

81 video4linux

89 i2c

90 mtd

116 alsa

128 ptm

136 pts

204 s3c2410_serial

252 s3c_bc

253 pvrsrvkm

254 rtc

Block devices:块设备

1 ramdisk

259 blkext

7 loop

8 sd

31 mtdblock

65 sd

66 sd

67 sd

68 sd

69 sd

70 sd

71 sd

128 sd

129 sd

130 sd

131 sd

132 sd

133 sd

134 sd

135 sd

179 mmc

254 device-mapper

2>然后根据你的设备个数分配次设备号,一般次设备号都从0开始

dev_t dev = MKDEV(主设备号,次设备号);

3>调用register_chrdev_region;向内核申请

注:主设备号不能为0

动态分配

调用alloc_chrdev_region 直接向内核申请

释放设备号

unregister_chrdev_region

linux字符设备驱动四个重要的数据结构

1>struct file

作用:描述文件打开以后的状态属性

生命周期:从open打开成功内核创建

到close关闭内核销毁

重要的成员:

const struct file_operations *f_op;

unsigned int f_flags;

//文件的操作属性

loff_t f_pos;

//文件的操作位置

2>struct file_operations

定义文件的操作函数集合,用户空间调用对应的系统调用时就会调用file结构体中file_operations成员中对应的函数

3>struct inode

作用:描述一个文件的物理结构

生命周期:文件存在,内核创建

文件销毁,内核销毁对应的inode

重要的成员:

dev_t i_rdev;

//存放设备号

struct cdev *i_cdev;

//指向一个字符设备

4>struct cdev

表示一个字符设备驱动

重要的数据成员:

const struct *ops;

//字符设备驱动的操作函数集合

dev_t dev;

//设备号

一个文件只有一个inode,可以有多个file

struct file如何找到cdev中的file_operations

1>应用程序调用open,最终调用sys_open

2>sys_open创建file结构体,描述文件的打开信息

3>通过主设备号找到对应的cdev

4>将cdev中的file_operations成员赋值给file中的file_operations成员

5>sys_open最后调用cdev中的file_operations中的open函数

6>以后用户空间对设备文件的所有操作访问的都是cdev中的file_operations成员的操作方法,也就是对硬件的操作

read --> sys_read --> file->f_op->read --> cdev->ops->read

write --> sys_write --> file->f_op->write --> cdev->ops->write

如何将cdev添加到内核

1>分配初始化 struct file_operations 文件的操作函数集合

struct file_operations cdd_fops = {

.open = ...

.read = ....

.write = ...

.....

};

2>分配初始化struct cdev 一个字符设备驱动

struct cdev cdd_cdev;

cdev_init(&cdd_cdev,&cdd_fops);

//cdd_cdev.ops = cdd_fops;

3>将cdev添加到内核

cdev_add(&cdd_cdev,设备号,设备个数);

结果就是将cdd_cdev添加到内核的cdev数组中,下标是以设备号为索引

一旦完成cdev的添加,内核中就有了一个真实的字符设备驱动

设备文件的创建

静态创建:

mknod cdd c 251 0

动态创建:

创建设备类:class_create

创建设备文件:device_create

实现一个字符设备驱动的步骤:

1>申请设备号(静态/动态)

2>注册cdev到内核

3>创建设备类

4>创建设备文件

使用字符设备驱动来操作LED

GPIO接口

内核定义了一系列的GPIO操作函数

申请GPIO:gpio_request

释放GPIO:gpio_free

将GPIO设置为输出功能:gpio_direction_output

将GPIO设置为输入功能:gpio_direction_input

设置GPIO的上下拉:s3c_gpio_setpull

S3C_GPIO_PULL_NONE

S3C_GPIO_PULL_DOWN

S3C_GPIO_PULL_UP

设置输出值:gpio_set_value

读取输入值:gpio_get_value

printf("key_val = %#x\n", key_val);//必须要有换行符,才能往终端上打印

printf 不是往终端上打印,而是往输出缓冲区打印