Linux设备驱动分类、字符设备驱动、如何将cdev添加到内核?
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 不是往终端上打印,而是往输出缓冲区打印
上一篇: 醉秋几许时光错,夕阳几度枫叶红
下一篇: 掬一捧秋水,煮一壶诗韵