设备文件
ls -lhi /dev可以看到设备文件
c代表字符设备,b代表块设备。看不到文件大小,可以看到主次设备号.
值得注意的是,设备文件只是通过文件io方式读写,这个过程其实是产生一些列调用过程,最终调用内核对应具体函数接口,对设备进行操作
#include<linux/fs.h>
一个文件必然在内核有对应的struct inode,这个结构体里面有设备号(dev_t—无符号整型)和指向一个字符设备驱动的设备指针(* 结构体指针)。1个结构体占了256字节,但是和驱动相关的只有这两个。struct
struct inode {
...
dev_t i_rdev; //设备号(dev_t---无符号整型)
struct cdev *i_cdev; //指向一个字符设备驱动的设备指针(* 结构体指针)
...
}
主设备号是说明是某一个类型设备,多个相同设备有可能主设备号相同,次设备号用于区分不同设备。主次设备号一共占32位;主设备号高12位,次设备号低20位

应用层通过open打开设备文件一次,会在内核层用内核的open创建一个structure file结构体,打开几次,内核里面就会对应几个struct file结构体。虽然对外表现为返回一个整型文件描述符。但是内核实际上是维护一个struct file结构体。对文件的操作首先要open,其余操作文件的函数接口内核里都是通过struct file 对象访问对应文件。
内核中存在一些驱动函数集,存储在struct file_operations结构体中,代表了一类设备的驱动接口用于

注册字符设备驱动的第一个函数接口,通过查看内核源码学习了解
1.注册
register_chrdev(主设备号、设备名、已经实例化好的驱动函数集)

如何赋值?
因为只能使用没用过的设备号,所以先查当前使用过的设备号:cat /proc/devices
/proc/devices`是Linux操作系统中的一个特殊文件,它提供了当前系统中已加载的设备驱动程序的列表。该文件列出了所有已注册的设备和对应的主、次设备号。
在Linux系统中,设备驱动程序通常会通过调用函数`register_chrdev()`或`register_chrdev_region()`来注册自己。这样就会分配一个唯一的主设备号给该驱动程序,并可以通过次设备号来区分不同的设备实例。
`/proc/devices`文件列出了已注册驱动程序所使用的主、次设备号以及设备的类型(character或block)。通过查看该文件,可以了解当前系统中有哪些设备驱动程序已经注册成功,以及对应的设备号信息。
需要注意的是,`/proc/devices`文件并不是一个普通的存储文件,而是一个虚拟文件系统中的接口,它提供了对内核数据的访问。因此,读取`/proc/devices`文件将获得动态生成的设 备信息,反映了当前系统的实际情况。
已经使用的主设备号不能使用,因为它代表了一类驱动,一类设备。主设备号如何和一类驱动关联?
struct cdev包含驱动函数集struct file_operations *ops,包含dev_t设备号
struct char_device_struct包含struct cdev *cdev
*chrdevs[255]是一个全局结构体指针数组, 存的是char_device_struct
register_chrdev包含/调用函数int __register chrdev, 这个函数再指向char_device_struct
name不是要生成的设备文件名,而是调试的时候用的,在cat /proc/devices下能看到
这个函数的第二三个函数已经被固定,0代表次设备号;256代表次设备号总个数,即使用register_chrdev注册的设备默认支持0-255的次设备号
查看设备号的指令: ls -l /dev/
统计行数:ls -l /dev/ | wc -l
注册十个温湿度传感器,这十个设备文件有相同的主设备号,不同的次设备号,而且通常情况次设备号连续。
在写代码的时候,需要手动添加register_chrdev函数,并给他传入3个参数(主设备号、设备名、已经实例化好的驱动函数集)
主设备号查询开发板空余的设备号即可,设备名可以根据功能和需要自定义,驱动函数集比较麻烦,我们也需要手动定义一下:
static int file_operations fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.read = led_read,
.release = led_close,
}
后四个led开头的函数需要我们自己实现,我们可以从内核源码fs.h的file_operations结构体中复制对应的函数名和参数,加以修改。以下是这四个驱动函数的实现:
static ssize_t
led_read (struct file *filp, char __user *buf, size_t cnt, loff_t *fpos)
{
if (cnt != 4) {
return -EINVAL;
}
}
/*buf是控制命令和灯的编号,write就是把buf里面的cont/cnt字节数写入内核文件filp*/
static ssize_t //用户通过这个函数来写入控制命令,例如亮灯还是灭灯,亮灭哪一个灯等等
led_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *fpos)
{
}
/*不是必要的,按需实现*/
static int led_open (struct inode *inodp, struct file *filp)
{
printk("------------- open maior: %d, minor: %d successful -------------\n",
imajor(inodp), iminor(inodp));//这两个函数传入的参数为inode,内容是从inode中获取设备号i_rdev,再分离出主次设备号返回回来
}
/*由上层的close调用*/
static int led_close (struct inode *inodp, struct file *filp)
{
printk("------------- close maior: %d, minor: %d successful -------------\n",
imajor(inodp), iminor(inodp));
}
2.移除
static void tiny4412_led_exit(void)
{
//让四个灯灭,
.....
//移除注册的驱动
....
}
插入到内核中
make通过提前准备的Makefile编译为.ko文件
挂载上位机到开发板,这样开发板才能访问到驱动文件
查看设备号空缺,insmod指令插入.ko文件/rmmod
手动创建设备文件: c/b后缀字符设备用c,块设备用d,只留一个就行
mknod /dev/leds c/b 80 0
创建成功后,就可以看到/dev/leds,主设备号80,次设备号0
接下来写应用程序appled.c
在app中调用驱动,在开发板运行app,就可以控制灯的亮灭
本文详细讲述了在Linux系统中设备文件的工作原理,包括设备号的组成、如何通过`register_chrdev`注册字符设备驱动,以及如何手动创建和操作设备文件。重点介绍了如何为温湿度传感器编写驱动程序,包括`file_operations`结构的使用和设备文件的创建过程。





