如同开始学C的部分,对于驱动的学习我们有必要记忆一些固定的东西
#include <linux/...> 包含相关的文件
这里要有两个相对必要的头文件
#include <linux/moudle.h>
#include <linux/init.h>
对应的函数
module_init(x);//这是个宏 __initcall(x);
module_exit(x);//这是个宏 __exitcall(x);
MOULE_LICENSE(_license);//这是个宏 MODULE_INFO(license, _license)
MODULE_INFO(license, _license) MODULE_INFO(license, _license)
MODULE_INFO(license, _license) MODULE_INFO(tag, info)
MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
__MODULE_INFO(tag, tag, info) __MODULE_INFO(tag, name, info)
__MODULE_INFO(tag, name, info)
static const char __UNIQUE_ID(name)[] \
__used __attribute__((section(".modinfo") , unused, aligned(1))) \
=__MODULE_INFO_PREFIX __stringify(tag) "=" info
...太多了
杂项设备
注意:1>注册杂项设备(misc_register)
2>根据misc_register函数要的参数创建结构体,
3>创建的结构体根据需要赋予初值
4>杂项主设备号默认为10,minor(次设备号),name 节点名, fops 文件相关操作的结构体
platform
1>注册paltform设备 (platform_driver_register(drv)
platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE)
__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv,struct module *owner)
因此drv 要传一个 struct platform_driver *类型的
或者传一个 struct platform_driver 类型的地址
2>注销paltform设备 void platform_driver_unregister(struct platform_driver * drv)
参数和注册相同
3.struct platform_driver drv 结构体
struct platform_driver
{
//probe函数指针 与设备树匹配会调用其指向的函数
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *); //remove函数指针
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; //该项重点
/#/ id_table 对应 platform 方法的匹配项,一般都用设备树有空可以了解一波
const struct platform_device_id * id_table;
bool prevent_deferred_probe;
};
4. struct device_driver
struct device_driver
{
const char *name; //在/sys/bus下创建目录的名字
struct bus_type *bus; //什么总线类型?
struct module *owner; //那个模块?
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table; //该项重点
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct attribute_group **dev_groups;
const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);
struct driver_private *p;
};
5.struct of_device_id
struct of_device_id
{
char name[32];
char type[32];
char compatible[128]; //平台总线匹配项 对应设备树的compatible
const void *data;
};
6. struct platform_driver int (*probe)(struct platform_device *) 指向的 prob函数中获取平台资源
相关内容
//平台总线
struct resource *platform_get_resource(struct platform_device *dev, \
unsigned int type, unsigned int num)
platform_device跟platform_driver的匹配
按照顺序有一个成功即完成匹配过程
a. 比较 platform_dev.driver_override 和 platform_driver.drv->name
b. 比较 platform_dev.dev.of_node的compatible属性 和 platform_driver.drv->of_match_table
c. 比较 platform_dev.name 和 platform_driver.id_table
d. 比较 platform_dev.name 和 platform_driver.drv->name//设备树
内核函数of_platform_default_populate_init, 遍历device_node树, 生成platform_device
设备树转换为platform dts -> dtb -> device_node -> platform_device
1.处理device_node
of.h // 提供设备树的一般处理函数
of_property_read_u32(读取某个属性的u32值)
of_get_child_count(获取某个device_node的子节点数)
of_address.h // 地址相关的函数of_get_address(获得reg属性中的addr, size值)
of_match_device(从matches数组中取出与当前设备最匹配的一项)
of_dma.h // 设备树中DMA相关属性的函数
of_gpio.h // GPIO相关的函数
of_graph.h // GPU相关驱动中用到的函数, 从设备树中获得GPU信息
of_iommu.h // 很少用到
of_irq.h // 中断相关的函数
of_mdio.h // MDIO (Ethernet PHY) API
of_net.h // OF helpers for network devices.
of_pci.h // PCI相关函数
of_pdt.h // 很少用到
of_reserved_mem.h // reserved_mem的相关函数2.处理 platform_device
of_platform.h // 把device_node转换为platform_device时用到的函数
of_device_alloc(根据device_node分配设置platform_device)
of_find_device_by_node (根据device_node查找到platform_device)
of_platform_bus_probe (处理device_node及它的子节点)of_device.h // 设备相关的函数
of_match_device
设备树匹配操作
//找节点
of_find_compatible_node 根据设备树的compatible属性找节点
struct device_node * of_find_compatible_node
(struct device_node *from, const char *type, const char *compat);
of_find_node_by_phandle 根据phandle找到节点
struct device_node* of_find_node_by_phandle(phandle handle);
传入NULL表示从根节点开始寻找
dts文件被编译为dtb文件时,每一个节点都有一个数字ID,这些数字ID彼此不同。可以使用数字ID来找到device_node。这些数字ID就是phandle//找属性
const void *of_get_property(const struct device_node *np, const char *name, int *lenp)
根据名字找到节点的属性,并且返回它的值
参数np表示节点,我们要在这个节点中找到名为name的属性,然后返回它的值
lenp用来保存这个属性的长度,即它的值的长度of_property_count_elems_of_size
根据名字找到节点的属性,确定它的值有多少个元素(elem)
//读取数据
a.读某个整数u32/u64
int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value);
在设备树中,节点大概是这样:
xxx_node {
name2 = <0x50000000 0x60000000>;
};
调用of_property_read_u32 (np, “name2”, 1, &val)时,val将得到值0x0x60000000b.读数组
int of_property_read_variable_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz_min, size_t sz_max);
int of_property_read_variable_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz_min, size_t sz_max);
int of_property_read_variable_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz_min, size_t sz_max);
int of_property_read_variable_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz_min, size_t sz_max);在设备树中,节点大概是这样:
xxx_node {
name2 = <0x50000012 0x60000034>;
};
上述例子中属性name2的值,长度为8
调用of_property_read_variable_u8_array (np, “name2”, out_values, 1, 10)时,out_values中将会保存这8个字节: 0x12,0x00,0x00,0x50,0x34,0x00,0x00,0x60
调用of_property_read_variable_u16_array (np, “name2”, out_values, 1, 10)时,out_values中将会保存这4个16位数值: 0x0012, 0x5000,0x0034,0x6000
总之,这些函数要么能取到全部的数值,要么一个数值都取不到
如果值的长度在sz_min和sz_max之间,就返回全部的数值;否则一个数值都不返回c.读字符串
int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string);
返回节点np的属性(名为propname)的值,(*out_string)指向这个值,把它当作字符串