目的:为了提高软件的重用,跨平台性能!!!
控制器驱动和设备驱动分离!!!
将驱动分离:主机控制器驱动和设备驱动,主机控制器驱动一般是半导体厂家写的。在linux驱动框架下编写具体的设备驱动。
二、驱动-总线-设备驱动和设备。
驱动就是具体的设备驱动。
设备:是设备属性,包括地址范围,如果是IIC器件地址,速度。
总线主要完成总线下设备和驱动之间的匹配。
驱动驱动数据类型为device_driver,驱动程序向内核注册驱动采用driver_register。
向总线注册驱动的时候,会检查当前总线下的所有设备,有没有与此驱动匹配的设备,如果有的话就执行驱动里面的probe函数。使用driver_register注册驱动。
设备数据类型为device,通过device_register向内核注册设备。
驱动与设备匹配以后驱动的probe函数就会执行,probe函数就是驱动编写人员去编写的!!!!
1、方便开发,linux提出了驱动分离与分层。
2、进一步引出了驱动-总线-设备驱动模型,或者框架。
3、对于SOC内部的RTC,timer等等不好归结为具体的总线,为此linux内核提出了一个虚拟总线:platform总线,platform设备和platform驱动。
注册的内容就是:
struct bus_type platform_bus_type = {
.name = “platform”,
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
}
对于platform平台而言,platform_match函数就是负责驱动和设备的匹配。
1、无设备树的时候,此时需要驱动开发人员编写设备注册文件,使用platform_device_register函数注册设备。
2,有设备树,修改设备树的设备节点即可。当设备与platform的驱动匹配以后,就会执行platform_driver->probe函数。
使用platform_driver_register向内核注册platform驱动。
platform_driver_register
-> __platform_driver_register (platform_driver)
-> 设置driver的probe为platform_drv_probe, //如果platform_driver的
// probe函数有效的话。
-> driver_register
->执行device_drive->probe,对于platform总线,也就是platform_drv_probe函数。而platform_drv_probe函数会执行platform_driver下的probe函数。
结论:向内核注册platform驱动的时候,如果驱动和设备匹配成功,最终会执行platform_driver的probe函数。
五、platform匹配过程根据前面的分析,驱动和设备匹配是通过bus->match函数,platform总线下的match函数就是:platform_match。
platform_match
-> of_driver_match_device,设备树
-> acpi_driver_match_device ACPI类型的
-> platform_match_id 根据platform_driver-> id_table
-> strcmp(pdev->name, drv->name) //最终的就是比较字符串,就是platform_device->name,和platform_driver->driver->name。无设备树情况下使用。
1、有设备树的时候:
of_driver_match_device
-> of_match_device(drv->of_match_table, dev) //of_match_table非常重要,
类型为of_device_id。
//compatible属性
两部分:platform_driver、platform_device。以点灯为例。
1、编写向platform总线注册设备。编写驱动需要寄存器地址信息,地址信息使用设备信息,定义在platform_device里面,因此需要在驱动里面获取设备中的信息,或者叫资源。使用函数platform_get_resource()
有设备树的时候设备device是由设备树描述的,因此不需要向总线注册设备,而是直接修改设备树。只需要修改设备树,然后编写driver驱动。
驱动和设备匹配成功以后,设备信息就会从设备树节点转为platform_device结构体。
platform提供了很多API函数去获取设备相关信息的。
2022.6.10 左神手撕代码 P57
设备加载cd /sys/bus/
cd platform/
后期驱动不需要修改 只需要改设备。
设备代码:
...... #include驱动代码:#include #define CCM_CCGR1_BASE (0x020C406C) #define SW_MUX_GPIO1_IO03_BASE (0x020E0068) #define SW_PAD_GPIO1_IO03_BASE (0x020E02F4) #define GPIO1_DR_BASE (0x0209C000) #define GPIO1_GDIR_BASE (0x0209C004) #define REGISTER_LENGTH 4 void leddevice_release(struct device *dev) { printk("leddevice releasern"); } static struct resource led_resources[] = { [0] = { .start = CCM_CCGR1_BASE, .end = (CCM_CCGR1_BASE + REGISTER_LENGTH -1), .flags = IORESOURCE_MEM, }, [1] = { .start = SW_MUX_GPIO1_IO03_BASE, .end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH -1), .flags = IORESOURCE_MEM, }, [2] = { .start = SW_PAD_GPIO1_IO03_BASE, .end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH -1), .flags = IORESOURCE_MEM, }, [3] = { .start = GPIO1_DR_BASE, .end = (GPIO1_DR_BASE + REGISTER_LENGTH -1), .flags = IORESOURCE_MEM, }, [4] = { .start = GPIO1_GDIR_BASE, .end = (GPIO1_GDIR_BASE + REGISTER_LENGTH -1), .flags = IORESOURCE_MEM, }, }; static struct platform_device leddevice = { .name = "imx6ull-led", .id = -1,//表示设备无此ID .dev = { .release = leddevice_release, }, .num_resources = ARRAY_SIZE(led_resources), .resource = led_resources, }; static int __init leddevice_init(void) { return platform_device_register(&leddevice); } static void __exit leddevice_exit(void){ platform_device_unregister(&leddevice); } module_init(leddevice_init); module_exit(leddevice_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("qhy");
.... #include应用层代码#define LEDDEV_CNT 1 #define LEDDEV_NAME "platled" #define LEDOFF 0 #define LEDON 1 static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; struct leddev_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; }; struct leddev_dev leddev; void led0_switch(u8 sta) { u32 val = 0; if(sta == LEDON){ val = readl(GPIO1_DR); val &= ~(1 << 3); writel(val, GPIO1_DR); }else if(sta == LEDOFF){ val = readl(GPIO1_DR); val|= (1 << 3); writel(val, GPIO1_DR); } } static int led_open(struct inode *inode, struct file *filp) { filp->private_data = &leddev; return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue; unsigned char databuf[1]; unsigned char ledstat; retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0) { return -EFAULT; } ledstat = databuf[0]; if(ledstat == LEDON) { led0_switch(LEDON); }else if(ledstat == LEDOFF) { led0_switch(LEDOFF); } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .write = led_write, }; static int led_probe(struct platform_device *dev) { //printk("led driver probern"); int i = 0; //int ret = 0; int ressize[5]; u32 val = 0; struct resource *ledsource[5]; printk("led driver and device has matched!rn"); for (i = 0; i < 5; i++) { ledsource[i] = platform_get_resource(dev,IORESOURCE_MEM,i); if (ledsource[i] == NULL) { dev_err(&dev->dev, "No MEM resource for always onn"); return -ENXIO; } ressize[i] = resource_size(ledsource[i]); // } IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start,ressize[0]); SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start,ressize[1]); SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start,ressize[2]); GPIO1_DR = ioremap(ledsource[3]->start,ressize[3]); GPIO1_GDIR = ioremap(ledsource[4]->start,ressize[4]); val = readl(IMX6U_CCM_CCGR1); val &= ~(3 << 26); val |= (3 << 26); writel(val, IMX6U_CCM_CCGR1); writel(5, SW_MUX_GPIO1_IO03); writel(0x10B0, SW_PAD_GPIO1_IO03); val = readl(GPIO1_GDIR); val &= ~(1 << 3); val |= (1 << 3); writel(val, GPIO1_GDIR); val = readl(GPIO1_DR); val |= (1 << 3) ; writel(val, GPIO1_DR); if (leddev.major) { leddev.devid = MKDEV(leddev.major, 0); register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME); } else { alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); leddev.major = MAJOR(leddev.devid); } leddev.cdev.owner = THIS_MODULE; cdev_init(&leddev.cdev, &led_fops); cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT); leddev.class = class_create(THIS_MODULE, LEDDEV_NAME); if (IS_ERR(leddev.class)) { return PTR_ERR(leddev.class); } leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME); if (IS_ERR(leddev.device)) { return PTR_ERR(leddev.device); } return 0; } static int led_remove(struct platform_device *dev) { iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); cdev_del(&leddev.cdev); unregister_chrdev_region(leddev.devid, LEDDEV_CNT); device_destroy(leddev.class, leddev.devid); class_destroy(leddev.class); return 0; } static struct platform_driver led_driver = { .driver = { .name = "imx6ull-led", //驱动名字和设备名字匹配 }, .probe = led_probe, .remove = led_remove, }; static int __init leddriver_init(void) { return platform_driver_register(&led_driver); } static void __exit leddriver_exit(void) { platform_driver_unregister(&led_driver); } module_init(leddriver_init); module_exit(leddriver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("qhy");
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define LEDOFF 0
#define LEDON 1
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char databuf[2];
if(argc != 3){
printf("Error Usage!rn");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!rn", argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]);
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control Failed!rn");
close(fd);
return -1;
}
retvalue = close(fd);
if(retvalue < 0){
printf("file %s close failed!rn", argv[1]);
return -1;
}
return 0;
}
有设备树情况下:
有设备树的情况下,设备是由设备树描述的,不需要向总线注册设备,而是直接修改设备树。
只需要修改设备树,然后编写驱动。
设备树配置!!!
左手撕 P2022.6.11
驱动和设备匹配成功后,设备信息就会从设备树节点转成platform_device结构体。
platform 提供很多获取资源的函数。
代码分解 头文件包含#include字符设备创建#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
#define LED_NAME "gpioled"
#define LED_COUNT 1
#define LEDOFF 0
#define LEDON 1
struct gpioled_dev {
struct cdev cdev;
struct class *class;
struct device *device;
dev_t devid; //设备号
int major; //主设备号
int minor; //次设备号
struct device_node *nd; //设备节点
int led_gpio;
};
struct gpioled_dev gpioled; //led设备
驱动函数
static int gpioled_open(struct inode *inode,struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;
int retvalue = 0;
unsigned char databuf[1];
retvalue = copy_from_user(databuf,buf,cnt);
if (retvalue < 0)
{
printk("kernel write failed!rn");
return -EFAULT;
}
if (databuf[0] == LEDON)
{
gpio_set_value(dev->led_gpio,0);
}
else if(databuf[0] == LEDOFF){
gpio_set_value(dev->led_gpio,1);
}
return 0;
}
static int gpioled_release(struct inode *inode,struct file *filp)
{
//struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.write = gpioled_write,
.open = gpioled_open,
.release = gpioled_release,
};
led_probe(字符设备创建流程)
struct of_device_id led_of_match[] = {
{.compatible = "alientek,gpioled"},
{},
};
static int led_probe(struct platform_device *dev)
{
int ret = 0;
gpioled.major = 0;
if (gpioled.major){
gpioled.devid = MKDEV(gpioled.major,0);//设备号
ret = register_chrdev_region(gpioled.devid,LED_COUNT,LED_NAME);
} else{
ret = alloc_chrdev_region(&gpioled.devid,0,LED_COUNT,LED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}printk("led major = %d led minor = %d rn",gpioled.major,gpioled.minor);
if (ret < 0){
goto faile_devid;
}
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev,&led_fops);
ret = cdev_add(&gpioled.cdev,gpioled.devid,LED_COUNT);
if(ret<0){
goto fail_cdev;
}
gpioled.class = class_create(THIS_MODULE,LED_NAME);
if(IS_ERR(gpioled.class)){
ret = PTR_ERR(gpioled.class);
printk("can't class_create rn");
goto fail_class;
}
gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,LED_NAME);
if(IS_ERR(gpioled.device)){
ret = PTR_ERR(gpioled.device);
printk("can't device_create rn");
goto fail_device;
}
//通过platform
gpioled.nd = dev->dev.of_node;
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-goios",0);
if (gpioled.led_gpio < 0)
{
printk("can't find gpio rn");
goto fail_findnode;
}
printk("led gpio number = %drn",gpioled.led_gpio);
ret = gpio_request(gpioled.led_gpio,"led-gpio");
if (ret)
{
printk("gpio_request errno rn");
ret = -EINVAL;
}
ret = gpio_direction_output(gpioled.led_gpio,1);
if (ret)
{
goto fail_setoutput;
}
gpio_set_value(gpioled.led_gpio,0);
printk("led_probern");
return 0;
fail_setoutput:
gpio_free(gpioled.led_gpio);
fail_findnode:
device_destroy(gpioled.class,gpioled.devid);
fail_device:
class_destroy(gpioled.class);
fail_class:
cdev_del(&gpioled.cdev);
fail_cdev:
unregister_chrdev_region(gpioled.devid,LED_COUNT);
faile_devid:
return ret;
}
led_remove函数
static int led_remove(struct platform_device *dev)
{
gpio_set_value(gpioled.led_gpio,1);
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid,LED_COUNT);
device_destroy(gpioled.class,gpioled.devid);
class_destroy(gpioled.class);
gpio_free(gpioled.led_gpio);
return 0;
}
platform_driver 函数
static struct platform_driver led_driver = {
.driver = {
.name = "imx6ull-led", //无设备树和设备进行匹配,驱动名字
.of_match_table = led_of_match,//设备树匹配表
},
.probe = led_probe,
.remove = led_remove,
};
模块加载与卸载
static int __init leddriver_init(void)
{
return platform_driver_register(&led_driver);
}
static void __exit leddriver_exit(void)
{
platform_driver_unregister(&led_driver);
}
//模块加载函数
module_init(leddriver_init);
//模块卸载
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");



