背景:管中窥豹2.4的驱动模型,明白进程怎么调用驱动程序的
2.6以后不用去使用 mknode创建设备文件
设备还是在 /proc/devices中
设备文件都放在/sys/ 中
设备节点为 /dev/设备节点
https://blog.csdn.net/guet_kite/article/details/78368928
扫盲
结构体和对象扫盲
key_set : kset是同类型kobject对象的集合,可以说是一个容器。
Kobject是基本数据类型,每个Kobject都会在"/sys/“文件系统中以目录的形式出现。是总线、驱动、设备的三种对象的一个基类,实现公共接口。
ktype,记录了kobject对象的一些属性。比如直接设置led灯的亮度属性/sys/led_key_set/led_kobject/led_ktype
class对象 /sys/class/xxx
device对象 /sys/class/xxx/yyy
自定义总线 /sys/bus/xbus 自己生成的总线,和IIC这边匹配类似
自定义总线dev /sys/bus/xbus/device/zzz
自定义总线drv /sys/bus/xbus/drv/zzz
平台总线 sys/bus/plantform
目录位置
设备 /proc/devices
设备文件 /sys/
设备节点 /dev/
结构体
基础结构体,设备文件创建
struct kset // 继承kobj 创建keyset的时候 也会创建kset->kobj, 也用来帮kobj和用户空间沟通
struct kobject //基础结构体,创建目录
struct kobj_type //记录属性文件调用接口
struct kobj_attribute //属性对象,和ktype调用,变成一个属性文件
设备节点创建
struct class //记录创建的类
struct device//记录上面类里创建的设备,
设备创建
struct char_device_struct //proc/devices/xxx有关 看00文章
struct cdev //proc/devices/xxx有关 看00文章
自建总线相关
struct bus_type //自己建立总线结构体
struct device //用于匹配自定义device
struct device_driver //用于匹配自定义drv
plnatfrom总线相关
struct platform_device //plnatfrom平台设备
struct platform_driver // plnatfrom平台driver
设备树相关
struct device_node //设备树的每个节点用这个结构体表示
struct property //获取节点里的属性用这个结构体表示
代码大概流程
设备文件创建 操作
创建 /sys/kset_test/led_kobject 这样一个设备文件夹 里面有设备属性文件 foo和led
用户空间通过直接cat和echo 操作这两个属性 对应着控制驱动空间
/* __ATTR 定义在 include/linux/sysfs.h。
* show成员 和 store成员 最终分别会被 kobject->ktype 下的 kobj_sys_ops 下的。
* kobj_attr_show 和 kobj_attr_store 调用。
* foo 对应属性文件名。
*/
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
/* led 对应属性名
led_show 对应open后的read 也对应cat
led_store 对应open后的write 也对应echo xx >/sys/kset_test/led_kobject/led
*/
static struct kobj_attribute led_attribute =
__ATTR(led, 0664, led_show, led_store);
/* 下面是attr一维数组的指针
每个attr对应一个文件夹中的一个文件*/
static struct attribute *attrs[] = {
&foo_attribute.attr, //对应 /sys/kset_test/led_kobject/foo
&led_attribute.attr, //对应 /sys/kset_test/led_kobject/led
NULL, /* need to NULL terminate the list of attributes */
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static int __init led_init_test(void) {
/* kset_create_and_add()
* 1.创建了一个key_set对象my_kset
* 2.同时创建了kobject对象 kset_test 存于my_kset->kobj
* 3.映射kobject对象(my_kset->kobj )在文件目录 /sys/kset_test
*/
my_kset = kset_create_and_add("kset_test", NULL, NULL);
/*kobject_create_and_add()
* 1.创建kobject对象led_kobj
* 2.指明父kobject节点为my_kset->kobj
* 3.文件目录映射在父节点目录下 ,此时创建led_kobj 的文件目录 /sys/kset_test/led_kobject
*/
led_kobj =kobject_create_and_add("led_kobject", &my_kset->kobj );
/* 给这个kobj创建创建属性attr
同时会在/sys/kset_test/led_kobject目录下生成属性文件
attr_group结构体里有attrs的双重指针,给每个attrs创建操作文件
*/
sysfs_create_group(led_kobj, &attr_group);
}
设备节点注册流程
平时想用app调用驱动 都会进行操作设备节点/dev/NEWCHRLED_NAME
下面是一个设备节点怎么被创建的
#define NEWCHRLED_NAME "newchrled" /* 名字 */
static int __init led_init(void)
{
//分配设备号
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT,NEWCHRLED_NAME);
cdev_init(&newchrled.cdev, &newchrled_fops);
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
/*class_create()
* 1.创建kobj对象 名字NEWCHRLED_NAME
* 2.给kobj映射文件目录 /sys/class/NEWCHRLED_NAME/
* 3.返回class对象,class对象其实也间接继承kobj 也就是kobj
*/
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
/*device_create()
* 1.创建kobj对象 名字NEWCHRLED_NAME
* 2.kob_j的父konj对象设置为class
* 3.给kobj映射文件目录 /sys/class/NEWCHRLED_NAME/NEWCHRLED_NAME
* 4.返回device对象,device对象其实也间接继承kobj 也就是kobj
* 5.创建/sys/class/NEWCHRLED_NAME/NEWCHRLED_NAME/dev 属性文件 存放硬件设备号
* 6.使用kobject_uevent() 通知用户空间守护进程udev 读取/sys/class/NEWCHRLED_NAME/NEWCHRLED_NAME/dev 属性文件
* 7.udev通过读取属性文件 创建设备节点 /dev/NEWCHRLED_NAME
*/
newchrled.device = device_create(newchrled.class, NULL,newchrled.devid, NULL, NEWCHRLED_NAME);
}
//注册完设备节点 app通过open,read,write操作驱动的file_operation结构体
自定义总线操作
自定义一个总线 叫x-bus
在里面增加dev和drv 匹配成功后 调用drv的probe
进行基本的设备文件创作和设备节点创作
/sys/bus/xbus 自己生成的总线,和IIC这边匹配类似
自定义总线dev /sys/bus/xbus/device/zzz
自定义总线drv /sys/bus/xbus/drv/zzz
bus_register(struct bus_type *bus) //添加新的总线类型
device_register(struct device);
driver_register(struct device_driver);
匹配成功后 再执行创建设备节点
plantform平台总线操作
平台总线相对于自定义总线 最大的特点是封装了一层 而且linux自动帮你注册这个总线 /sys/bus/plantform
让drv 能更方便的获取 dev的硬件资源 并且linux帮你写好了 总线match函数 方便匹配设备树 或id表等
所以struct platform_device 继承上面 struct device
struct platform_driver 继承上面 struct device_driver
platform_device_register(struct platform_device); /sys/bus/plantform/dev/xxx
platform_driver_register(struct platform_driver); /sys/bus/plantform/drv/xxx
匹配成功后 再执行创建设备节点
设备树属性查看
文章写了 翻一下
ls /sys/firmware/devicetree/base
子系统介绍
pinctrl
前面写的驱动程序 控制硬件外设的时候 需要知道硬件外设有哪些gpio 要去看数据手册 看gpio有哪些寄存器 再去配置复用寄存器 这样效率就很低 pinctrl子系统驱动 根据设备树iomuxc节点选择状态进行配置
iomuxc节点(设备树节点)
- 汇总所需引脚的配置信息
- pinctrl子系统预存iomux节点信息
-各种引脚组合的信息,比如串口三个引脚 直接打包为一组放在iomuxc
其他驱动程序需要信息 要去pinctr子系统 把iomuxc的引脚信息取出来
后面太多了 没有看!!! 就看了两节
gpio子系统
在驱动中 一个引脚复用为gpio
想直接控制这个引脚 用gpio子系统开发的接口就可以了
轻松做输入输出 获取当前值