网站制作帐户设置,苏州建设公司网站建设,wordpress 微云插件,wordpress 后台 获取分类id目录
一、Linux驱动的分离
二、Linux驱动的分层
三、platform平台驱动模型简介
3.1 platform_driver结构体
3.2 device_driver结构体
3.3 platform驱动API函数
四、驱动代码 一、Linux驱动的分离
对于Linux这种庞大而复杂的系统#xff0c;需要非常注重代码的重用性需要非常注重代码的重用性否则Linux内核中将会存在大量无意义的重复代码。假设现在有三个平台A、B和C该三个平台都有MPU6050这个I2C接口的六轴传感器如果按照写裸机I2C驱动的思路每个平台都有一个MPU6050的驱动因此编写出来的最简单的驱动框架如图 如果再来几个I2C设备比如AT24C02、FT5206(电容触摸屏)等按照上图的写法那么设备端的驱动将再重复编写好几次。显然在Linux驱动程序中这种写法是不推荐的最好的做法就是每个平台的I2C控制器都提供一个统一的接口(主机驱动)每个设备也只提供一个驱动程序(设备驱动)每个设备通过统一的I2C接口驱动来访问这样就可以大大简化驱动文件: 此时如果再来几个I2C设备比如AT24C02、FT5206(电容触摸屏)等那么就是 这便是驱动的分隔即将主机驱动和设备驱动分隔开来比如I2C、SPI等等都会采用驱动分隔的方式来简化驱动的开发。 在实际的驱动开发中主机控制器驱动一般由半导体厂家编写好而设备驱动一般也由设备器件的厂家编写好我们只需要提供设备信息即可。 驱动只负责驱动设备只负责设备将两者进行匹配即可相当于将设备信息从设备驱动中剥离开来驱动使用标准方法去获取到设备信息比如从设备树中获取到设备信息然后根据获取到的设备信息来初始化设备——即Linux中的总线(bus)、驱动(driver)和设备(device)模型 当向系统注册一个驱动的时候总线就会在右侧的设备中查找看看有没有与之匹配的设备如果有的话就将两者联系起来同样的当向系统中注册一个设备的时候总线也会在左侧的驱动中查找看有没有与之匹配的设备有的话也联系起来。Linux内核中大量的驱动程序都采用总线、驱动和设备模式platform驱动就是这一思想下的产物。 二、Linux驱动的分层
网络有7层模型不同的层负责不同的内容。同样的 Linux下的驱动往往也是分层的分层的目的也是为了在不同的层处理不同的内容。
比如input子系统经常所使用到负责管理所有跟输入有关的驱动包括键盘、鼠标、触摸等最底层的就是设备原始驱动负责获取输入设备的原始值获取到的输入事件上报给input核心层。 input核心层会处理各种IO模型并且提供file_operations操作集合。在编写输入设备驱动的时候只需要处理好输入事件的上报即可至于如何处理这些上报的输入事件那是上层去考虑的。 三、platform平台驱动模型简介
platform驱动框架分为总线、设备和驱动其中总线是Linux内核提供的编写驱动时只需要关注设备platform_device和驱动platform_driver的具体实现即可。在无设备树的Linux内核下需要分别编写并注册platform_device和platform_driver在使用设备树时设备的描述被放到了设备树中因此platform_device不用编写只需要实现platform_driver即可。
3.1 platform_driver结构体
platform_driver结构体表示platform驱动此结构体定义在文件include/linux/platform_device.h中
struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe;
};probe函数当驱动与设备匹配成功以后probe函数就会执行。
driver成员device_driver结构体变量Linux内核里大量使用面向对象的思维device_driver相当于基类提供了最基础的驱动框架。plaform_driver继承了这个基类然后在此基础上又添加了一些特有的成员变量。
id_tableplatform总线匹配驱动和设备的时候采用的一种方法 id_table是个表 数组)每个元素的类型为platform_device_id
struct platform_device_id { char name[PLATFORM_NAME_SIZE]; kernel_ulong_t driver_data;
};3.2 device_driver结构体
device_driver结构体定义在include/linux/device.h
struct device_driver { const char *name; 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 */ 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 dev_pm_ops *pm; struct driver_private *p;
};of_match_table采用设备树时驱动所使用的匹配表数组每个匹配项都为of_device_id结构体类型此结构体定义在文件include/linux/mod_devicetable.h中内容如下
struct of_device_id { char name[32]; char type[32]; char compatible[128]; const void *data;
};compatible对于设备树而言通过设备节点的compatible属性值和of_match_table中每个项目的compatible成员变量进行比较如果有相等的就表示设备和此驱动匹配成功。
注意of_device_id表最后一个匹配项必须是空的
3.3 platform驱动API函数
在编写platform驱动时首先定义一个platform_driver结构体变量然后实现结构体中的各个成员变量重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后probe函数就会执行具体的驱动程序在probe函数里面编写比如字符设备驱动等等。
当我们定义并初始化好platform_driver结构体变量以后需要在驱动入口函数里面调用platform_driver_register函数向Linux内核注册一个platform驱动
int platform_driver_register (struct platform_driver *driver)
driver要注册的platform驱动。返回值负数即失败0即成功。
驱动卸载函数中通过platform_driver_unregister函数卸载platform驱动
void platform_driver_unregister(struct platform_driver *drv)
drv要卸载的platform驱动。返回值无。 四、驱动代码
platform驱动框架
struct xxx_dev{ struct cdev cdev; /* 设备结构体其他具体内容 */
}; struct xxx_dev xxxdev; /* 定义个设备结构体变量 */ static int xxx_open(struct inode *inode, struct file *filp)
{ /* 函数具体内容 */ return 0;
} static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{ /* 函数具体内容 */ return 0;
} /*
* 字符设备驱动操作集
*/
static struct file_operations xxx_fops { .owner THIS_MODULE, .open xxx_open, .write xxx_write,
}; /*
* platform驱动的probe函数
* 驱动与设备匹配成功以后此函数就会执行
*/
static int xxx_probe(struct platform_device *dev)
{ ...... cdev_init(xxxdev.cdev, xxx_fops); /* 注册字符设备驱动 */ /* 函数具体内容 */ return 0;
} static int xxx_remove(struct platform_device *dev)
{ ...... cdev_del(xxxdev.cdev);/* 删除cdev */ /* 函数具体内容 */ return 0;
} /* 匹配列表 */
static const struct of_device_id xxx_of_match[] { { .compatible xxx-gpio }, { /* Sentinel */ }
}; /*
* platform平台驱动结构体
*/
static struct platform_driver xxx_driver { .driver { .name xxx, .of_match_table xxx_of_match, }, .probe xxx_probe,.remove xxx_remove,
}; /* 驱动模块加载 */
static int __init xxxdriver_init(void)
{ return platform_driver_register(xxx_driver);
} /* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{ platform_driver_unregister(xxx_driver);
} module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE(GPL);