1.对platform平台总线的认识
1、平台总线:
1)相对于usb、pci、i2c等物理总线来说,platform总线是虚拟的、抽象出来的
2)CPU和外设通信方式:1.cpu内部的地址线端口链接,我们的platform平台总线对应链接SoC的内部外设
2.专用接口式链接,如通过usb协议链接usb设备
2、引入platform平台总线的好处:
1)使得设备被挂接在一个总线下
2)隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体配置信息;而在驱动中,只需要通过通用API去获取资源和数据。
3、platform平台总线的工作原理:
(1)第一步:系统启动时在bus系统中注册platform,platform_bus_init函数
(2)第二步:内核移植的人负责提供platform_device,调用platform_device_register注册
(3)第三步:写驱动的人负责提供platform_driver,调用platform_driver_register注册
(4)第四步:platform_match函数通过设备名name匹配driver和device,匹配后调用driver的probe函数安装,然后设备就工作起来了
对第四步的理解:
每种总线(不光是platform,usb、i2c那些也是)都会带一个match方法,match方法用来对总线下的device和driver进行匹配。platform总线的匹配函数为platform_match,platform_match工作原理:如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了,如果找完id_table都还没找到就说明没配上;如果没有id_table或者每匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败。
4、驱动如何获取设备数据,相关变量resource结构体、platform_get_resource函数
驱动通过platform_get_resource()这样的API来获取platform_device中的resource(设备资源),resource的定义也通常在BSP的板文件中进行
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};start、end和flags这3个字段决定了这个资源是属于中断、内存、DMA通道中的哪一种
自定义设备数据放在platform_date结构体中
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
return NULL;} //返回r用来存储设备的资源
5.设备驱动模型(设备和驱动组成的一个层次结构)
总线:链接各个设备,例如platform平台总线、usb总线,各个总线下有设备和驱动
设备:在设备/sys/devices/是创建真正的设备文件。而设备类/sys/class/和总线/sys/bus/(总线作用是连接所有设备)以下的子目录中出现的设备都是用符号链接指向/sys/devices/目录下的文件
类:class是一种人造概念,目的就是为了对各种设备进行分类管理
对LED驱动框架的认识
系统资源之GPIO
gpio资源是有限的,为了便于不同外设去使用,我们的做法是:每个驱动模块如果要使用某个GPIO就要先调用特殊的接口去申请,申请到后使用,使用完后要释放,从而引入了gpiolib来管理所有的gpio
内核开放出来的接口:
gpio_request 设备驱动中要想使用某一个gpio,就必须先调用gpio_request接口来向内核的gpiolib部分申请,得到允许后才可以 用这个gpio
gpio_direction_input/gpio_direction_output: 接口用来设置GPIO为输入/输出模式
writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT);读改写寄存器
gpiochip_add: 用于向内核注册我们的gpiolib
代码现状:
市场上不同芯片厂商都有自己的led驱动,但是总体上来说,led-class.c和led-core.c,内核开发者提供的;leds-xxxx.c,由不同厂商的驱动工程师根据自己硬件特点编写添加的。未引入gpiolib概念之前,我们的led_classdev_register其实就是led驱动框架中内核开发者提供给SoC厂家驱动开发者的一个注册驱动的接口,引入gpiolib后,注册接口变为gpiochip_add
字符设备驱动
file_operation结构体中元素是一个个函数指针,用来连接实际干活的函数
1.接口
register_chrdev_region()注册主次设备号、设备名
void cdev_init(struct cdev *cdev, const struct file_operations *fops)初始设备结构体,设备操作函数
//cdev_init可由下面//内的代码代替
struct cdev *p;
p== cdev_alloc();
p->owner=
p->ops= 实例化结构体元素中的file_operations变量(设备操作函数)
//
cdev_add(struct cdev *p, dev_t dev, unsigned count)向内核添加一个设备
2.页表的建立
1.静态映射表在arch/arm/plat-s5p/cpu.c中的s5p_iodesc,本质是一个结构体数组,数组中每一个元素就是一个映射,这个映射描述了一段物理地址到虚拟地址之间的映射。
2. 页表的建立iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc))
结构体数组s5p_iodesc[]所记录的几个映射关系被iotable_init()函数使用,将这个结构体数组格式的表建立成MMU所能识别的页表映射关系。
ioremap的实现将物理地址映射到页表,返回的内核虚拟地址(3G-4G)去访问之这段I/O内存资源,见https://blog.csdn.net/weixin_37726386/article/details/81697991