文章目录
1.kobject:/sys/bus/platform/device/里节点链接到/sys/devices/platform/节点
注意如下在/sys/dev目录下。
1.1 注册:sysfs文件系统
如下api创建/sys/bus目录下以自己名字命名的xxx-bus目录。
注册完bus后生成如下目录。
如下device目录下才是device_register注册生成。device_initialize包含device结构体kobject成员填充。
如下比device注册做的事情少很多,在/sys/bus/xxx-bus/drivers目录下创建自己名字目录。
创建链接指向module,表示该驱动是由哪个内核模块提供能力,同样module也反向指向驱动,表示它提供什么样的驱动能力。
driver匹配成功后不退出,因为挂在这总线上设备不一定只有一个,比如usb上挂鼠标,挂键盘。device只扫一个driver就停止。bus_for_each_drv扫的是klist_driver。
1.2 总线:name有i2c、spi、platform等,属性(需要填充)和方法(需要实现)
新的设备或驱动
register到总线中时就会调用match,设备和驱动匹配成功返回非0值。match函数是每个初始化bus总线需要实现的,因为不实现match函数表示这驱动是通用的。
如2.1章节platform_match先去解析设备即设备树,如以太网mdio bus的match匹配依据是读取phy设备寄存器
得到phyid去匹配驱动里列出了可支持的phyid
,匹配成功后执行probe才进行phy的初始化,每个总线的match函数不一样。
probe里进行设备初始化,解析资源如拿到中断,还要注册中断,remove是当设备从总线上移除调用释放资源,suspend和resume是设备低功耗相关。
如下自动扫描driver_autoprobe:1。
如上是往klist_devices里加东西,新成员同如下黄色。
1.3 设备:=设备树=总线=控制器=platform_device(对内核来说这些也是设备)
如下函数定义是在base文件夹里,platform文件夹会调此函数进行platform总线注册。device_add里包含:device结构体name填充,kobject_add创建文件,调bus_add_device函数挂入klist_device。
如下parent构建设备树层级结构,总线驱动程序(不是设备驱动程序)负责设置下面字段,如下包含driver。
1.4 驱动:driver结构体的属性和方法需要用户实现
static struct i2c_driver fcbcpld_driver = {
.probe = fcbcpld_probe, // 属性和方法,用户实现
.id_table = fcbcpld_id, // 和设备树进行匹配
...
}
static int32_t __init fcbcpld_mod_init(void) {
return i2c_add_driver(&fcbcpld_driver); //当driver注册进bus中,会去扫device链表匹配,反之一样。
}
module_init(fcbcpld_mod_init);
2.platform总线:挂在platform bus不单单是一个虚拟总线,platform驱动probe里还会注册如i2c_board_info,gpio控制器等实际总线设备驱动
实际存在的总线通过cpu bus直接寻址,访问对应的i2c设备对应的寄存器,linux基于内核的设备模型基础上即bus,device,device_driver进一步封装成platform_bus,platform_device,platform_driver,platform总线为实际的总线服务,便于驱动开发实际存在的总线。
match是由每个bus自己实现,如以太网有自己的match,最重要工作是driver和device的匹配,规则可以自己定,of开头都是设备树解析函数,所以每个设备树有自己的兼容性名字并且ok,都会在内核初始化时解析成platform_device。所以bus在driver注册时会去扫platform_device链表,反之一样,扫描时调的匹配规则就是下面函数,匹配成功后调用platform_driver里probe函数进行注册自己对应的i2c,spi,i2c_board_info填充,spi_board_info填充,把设备树里设备信息通过获取资源方法拿出来进行解析。
2.1
drv->driver调用上面device_driver。如下注册就是注册进klist_driver。
2.2
platform_device接触的不多,因为有设备树,设备树里只要写了兼容性名字,都会被内核解析成platform_device,最早没有设备树前,platform_device要自己注册,现在不用。资源(io,内存,中断)具有独占性,如这个中断不允许多个设备同时使用,因为在内核提供很多api用于分配和管理资源,resource结构体进行管理,组织这些资源名称、类型、起始和结束地址等。设备树里设备信息都被内核解析存储在resource资源里。
1.led子系统
led闪烁像pwm一样,亮多少秒,灭多少秒,pwm组合,led子系统都可以做到。
内核中doc目录下搜索linux,default-trigger。
如下led-class只是设置上面节点设置和查询。实际工作在led-core.c里,led-core.c又做了抽象,触发类型调到led-triggers.c,ledtrig-timer.c都是库函数,led-class.c相当于前台,led-core.c是软件层面工作,leds-gpio.c是对硬件层的操作如操作gpio。
2.面向对象继承思想
3.主次设备号
c后面有主次设备号如249,0。
设备号分配分为静态分配和动态分配,假如一个主设备号没有被占用,调用如下就可以注册,下面方法用的少,因为要先定义好主设备号且确保系统没有被占用,要不然这个驱动冗余度偏低,因为这驱动放其他系统上,主设备不一定和之前写的一样。
申请完后对下面结构体进行填充,供后面字符设备注册时使用。
如下CHRDEV_MAJOR_MAX=512,所以主设备号不能大于512
如下分为基础的主设备号和额外的extrend设备号
如下结构体为null的话,就是结构体里没有任何数据,后面字符设备的注册也是对这结构的填充,如下第一个成员是链表,所有字符设备很多链表管理
4.字符设备注册
dev_t是主设备号和次设备号的整形数字,count是添加几个设备。
如下往cdev_map全局变量里加。
下面函数主要功能就是cdev和上面data进行关联。linux内核设计尽量避免全局变量作为函数间数据传递方式,而是多使用形参列表。
下面最后一个参数是data,将 cdev *p添加到上面结构体的data中,最多添加255个字符设备。
如下while中有数据为真则往下传,为null跳出,插入p->next。
5.platform设备结构体
设备树compar匹配或status=ok,内核解析设备树时就会把每个设备树节点转为platform_device结构体的实例化内容,
6.gpio
7.文件io
当调用read,系统调用从文件中读取一些字节时,linux内核除了读取指定字节数据外,还会额外预读取一些数据到内核的缓存区里,下次再读取文件内容时,会先从内核缓存区里查找,如果找到,则省去等待慢磁盘定位和数据传输时间,大多数linux系统中预读取数据长度为128kb,也可根据系统可用的内存大小进行动态调整。
写入文件内容时,函数将数据回写到内核缓冲区后才进行返回,(read,wtite)内核在稍后一段时间内才会将内容写入磁盘中,除了更新文件内容外,还会更新元数据(文件大小,文件最后修改的时间)。
某段代码逻辑需要保证文件内容确实已经更新到磁盘后才可向下运行如关机,还有一个是自己实现了缓冲,不需要内核,如fread和fwrite自己实现了一套高速缓存(应用层),避免了反复系统调用(因为系统调用本身有开销),该应用层缓存大小如何去定?在ext2文件系统里已有所表现,应用缓存大小=所使用文件系统块大小(如4k字节),
调用如下再关机。open文件时可以传入标志(ODYSYNC和OSYNC),read/write也能达到下面效果。
8.输入子系统
9.spi
1.USB
2.磁盘缓存,页缓存
3.平均负载
4.fdisk
dd命令看存取速率
5.usb