• platform驱动模型再叙


    总线设备驱动简述
    • 因此很多时候我们不需要去为每个设备都写一个驱动,很多设备的驱动方式都是一样的,显然驱动是可以共用一套的。为了尽可能解决设备之前的兼容问题,Linux 之后又引入了基于 platform 驱动模型,该驱动模型将设备抽象为总线上的节点。
    • platform 模型又称为总线驱动模型,该模型将整个驱动主要分为三个部分,即总线、驱动、设备。这三个部分是独立的,也就是即使没有相应的驱动,设备也可以被加载,但可能无法正常工作,同理,即使总线不存在,驱动也可能存在。
    • 总线相当于胶水,驱动和设备相当于两个独立的物体,总线会设备和驱动粘合在一起,从而实现驱动和设备的绑定。Platform 驱动模型最大的好处就是可以让一个驱动可以与多个设备绑定,也就是多个设备可以共用一个驱动。
      在这里插入图片描述
    设备和驱动注册(未引入 dts)
    //====================================注册设备的步骤
    //定义硬件相关资源
    static struct resource led_resource[] =
    {
    	[0] = {
    		.start = 0x01c20891,/*GPIOE_CFG0*/
    		.end = 0x01c20893,
    		.flags = IORESOURCE_MEM,
    	},
    	[1] = {
    		.start = 0x01c20894,/*GPIOE_CFG1*/
    		.end = 0x01c20897,
    		.flags = IORESOURCE_MEM,
    	},
    	[2] = {
    		.start = 0x01c208A0,/*GPIOE_DATA*/
    		.end = 0x01c208A3,
    		.flags = IORESOURCE_MEM,
    	},
    	[3] = {
    		.start = 0x01c20891,/*GPIOE_PUL0*/
    		.end = 0x01c20893,
    		.flags = IORESOURCE_MEM,
    	},
    };
    static void led_release(struct device* dev)
    {
    	/*设备相关信息描述*/
    }
    //定义设备结构体
    static struct platform_device led_dev =
    {
    	.name = "led",//定义名字为led
    	.id   = -1;
    	.num_resources = ARRRAY_SIZE(led_resource);/*资源数量*/
    	.resource=led_resource,//资源结构体数组
    	.dev = {
    		.release=led_release,//设备注销
    	},
    }
    //注册设备,将其注册到platform_bus上
    static int led_dev_init(void)
    {
    	platform_device_register(&led_dev);
    	return 0;
    }
    //注销设备
    static void led_dev_exit(void)
    {
    	platform_device_unregister(&led_dev);
    }
    //指定模块入口和出口
    module(led_dev_init);
    module_exit(led_dev_exit);
    MODULE_LICENSE("GPL");
    
    /*
    * 上面的代码可以看到该驱动模型将设备和驱动分为两个不同的模块,分别加载到内核中
    * 发生了一个问题就是:硬件发生了变化避免不了需要修改代码重新编译内核
    * 设备数的引用就是避免了内核和设备树绑定在了一起,
    * 其实上述存放硬件的寄存器的resource数组来存放硬件的信息,完全时可以放在设备数树中获取的。
    * 完全不用在平台总线上匹配设备文件取获取硬件的信息
    */
    
    
    
    //====================================注册驱动的步骤
    //申请一个platform_driver结构体
    static struct platform_driver led_driver = 
    {
    	.probe=led_probe,
    	.remove=led_remove,
    	.driver = {
    		.name="led",//匹配设备的目的
    	},
    }
    //注册platform驱动
    static int __init led_init(void)
    {
    	ret = platform_driver_register(&led_driver);
    	return ret;
    }
    //注销platfomr驱动
    static void __exit led_exit(void)
    {
    	platform_driver_unregister(&led_driver);
    }
    //填充platform_driver结构体
    static dev_t led_dev_num;//定义一个设备号
    static struct cdev* led_dev;//定义一个设备管理结构体指针
    static struct class* led_class;//定义一个设备类
    static struct device* led0;//定义一个设备
    
    //设备硬件之间的寄存器地址
    size_t* gpioe_cfg0;//存储虚拟地址到物理地址
    size_t* gpioe_cfg1;//存储虚拟地址到物理地址
    size_t* gpioe_data;//存储虚拟地址到物理地址
    size_t* gpioe_pul0;//存储虚拟地址到物理地址
    
    static struct file_operations led_ops = {//应用层用内核层驱动之间的桥梁
    	.owner=THIS_MODULE,
    	.open=led_open,
    	.read=led_read,
    	.write=led_write,
    	.release=led_close,
    };
    
    //一旦与平台设备匹配就立即执行此函数进行平台驱动的初始化与注册
    static int led_probe(struct platform_device* pdev)
    {
    	struct resource* res;
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取device中的resource资源
    	gpioe_cfg0 = ioremap(res->start,(res->end - res->start)+1);
    	alloc_chrdev_region(&led_dev_num, 0, 1, "led");//动态申请设备号
    	
    	led_dev->owner = THIS_MODULE;//初始化设备管理结构体的owner为THIS_MODULE
    	led_dev->ops = &led_ops;//初始化设备操作函数指针为led_ops函数
    	
    	cdev_add(led_dev, led_dev_num, 1)//将设备添加到内核中
    
    	led_class = class_create(THIS_MODULE,"led_class");//创建一个名字为led_class的类
    	led0 = device_create(led_class, NULL, led_dev_num, NULL, "led0");//创建一个设备名字led0
    	return 0;
    }
    //remove函数
    static int led_remove(struct platform_device* pdev)
    {
    	cdev_del(led_dev);//从内核中删除设备管理结果体
    	unregister_chrdev_region(led_dev_num,1);//注销设备号
    	device_destroy(led_class, led_dev_num);//删除设备结点
    	class_destroy(led_class);//删除设备类
    	iounmap(gpioe_cfg0);//取消寄存器的映射
    	return 0;
    }
    //指定模块入口和出口
    module_init(led_init);
    module_exit(led_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    设备和驱动注册(引入 dts)
    • 内核自动将设备数上的设备节点(devicenode)–》平台设备(platform_device)
    //添加 dts 设备节点
    led@0x01c20800{
    	compatible="lite200,led"
    	reg = < 0x01C20890 0x04 /* GPIOE_CFG0 */
    			0x01C20894 0x04 /* GPIOE_CFG1 */
    			0x01C208A0 0x04 /* GPIOE_DATA */
    			0x01C208AC 0x04 >; /* GPIOE_PUL0 */
    	status = "okay";
    }
    
    //定义一个驱动结构体(该结构体用来注册 platform 驱动)
    static struct platform_driver led_driver =
    {
    	.probe = led_probe,//该函数中一般用来初始化设备驱动和注册 platform 驱动。
    	.remove = led_remove,//一般设备驱动的释放和注销
    	.driver = {
    		.name = "led",
    		.of_match_table = led_match_table,//of_match_table 是一个列表,用来匹配多个 compatible 属性。
    	},
    	.id_table = led_device_ids,//因此这里可以用 id_table 来指定可以兼容的设备名。
    };
    
    //定义 probe 函数(probe 函数是当驱动与设备树节点匹配后会执行的函数)
    static int led_probe(struct platform_device* pdev)
    {
    	struct resource* res;
    	int ret;
    	led_dev = cdev_alloc(); //动态申请一个设备结构体
    
    	ret = alloc_chrdev_region(&led_dev_num, 0, 1, "led"); //动态申请一个设备号
    
    	led_dev->owner = THIS_MODULE; //初始化设备管理结构体的 owner 为 THIS_MODULE
    	led_dev->ops = &led_ops; //初始化设备操作函数指针为 led_ops 函数
    
    	cdev_add(led_dev, led_dev_num, 1); //将设备添加到内核中
    
    	led_class = class_create(THIS_MODULE, "led_class"); //创建一个类
    	led0 = device_create(led_class, NULL, led_dev_num, NULL, "led0"); //创建一个设备
    	
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取 device 中的 GPIOE_CFG0
    	gpioe_cfg0 = ioremap(res->start, (res->end - res->start) + 1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 1); //获取 device 中的 GPIOE_CFG1
    	gpioe_cfg1 = ioremap(res->start, (res->end - res->start) + 1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 2); //获取 device 中的 GPIOE_DATA
    	gpioe_data = ioremap(res->start, (res->end - res->start) + 1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 3); //获取 device 中的 GPIOE_PUL0
    	gpioe_pul0 = ioremap(res->start, (res->end - res->start) + 1)
    
    	return 0;
    }
    
    //定义 of_match_table 结构体(用来匹配设备,当内核解析设备后会生成platform_device设备)
    //总线会根据这个表来进行匹配,如果有相同的,则将该设备与驱动进行绑定,同时执行驱动的 probe 函数来试图挂载驱动。
    static struct of_device_id led_match_table[] = {
    	{".compatible = lite200,led",},
    };
    
    //定义 id_table 结构体(一般情况下一个驱动可以匹配多个设备)
    static struct platform_device_id led_device_ids[] = {
    	{.name="led",},
    };
    
    //定义 remove 函数(该函数需要实现当驱动移除时候所作的一些工作,一般是需要对设备进行释放)
    static int led_remove(struct platform_device* pdev) 
    {
    	iounmap(gpioe_cfg0); //取消 GPIOE_CFG0 映射
    	iounmap(gpioe_cfg1); //取消 GPIOE_CFG1 映射
    	iounmap(gpioe_data); //取消 GPIOE_DATA 映射
    	iounmap(gpioe_pul0); //取消 GPIOE_PUL0 映射
    	
    	cdev_del(led_dev); //从内核中删除设备管理结构体
    	
    	unregister_chrdev_region(led_dev_num, 1); //注销设备号
    	
    	device_destroy(led_class, led_dev_num); //删除设备节点
    	class_destroy(led_class); //删除设备类
    	
    	return 0;
    }
    
    //定义字符设备结构体(们必须要为应用程序提供一个操作结构)
    static struct file_operations led_ops = {
    	.owner = THIS_MODULE,
    	.open = led_open,
    	.read = led_read,
    	.write = led_write,
    	.release = led_close,
    };
    
    //实现各个操作函数-对上述结构体进行实例化
    static int led_open(struct inode* inode, struct file* file) {
    	/* GPIOE 配置 */
    	*((volatile size_t*)gpioe_cfg1) &= ~(7 << 16); //清除配置寄存器
    	*((volatile size_t*)gpioe_cfg1) |= (1 << 16); //配置 GPIOE12 为输出模式
    	*((volatile size_t*)gpioe_pul0) &= ~(3 << 16); //清除上/下拉寄存器
    	*((volatile size_t*)gpioe_pul0) |= (1 << 12); //配置 GPIOE12 为上拉模式
    	printk(KERN_DEBUG"open led!!!\n");
    	return 0;
    }
    static int led_close(struct inode* inode, struct file* filp) {
    	/* GPIOE 配置 */
    	printk(KERN_DEBUG"close led!!!\n");
    	return 0;
    }
    static int led_read(struct file* filp, char __user* buff, size_t count, loff_t* offp) {
    	int ret;
    	size_t status = *((volatile size_t*)gpioe_data);//获取 GPIOE12 状态
    	ret = copy_to_user(buff, &status, 4); //将内核空间拷贝到用户空间 buff
    	if (ret < 0)
    		printk(KERN_DEBUG"read error!!!\n"); //输出信息
    	else
    		printk(KERN_DEBUG"read led ok!!!\n"); //输出信息
    	return 0;
    }
    static int led_write(struct file* filp, const char __user* buff, size_t count, loff_t
    	* offp) {
    	int ret;
    	size_t status;
    	ret = copy_from_user(&status, buff, 4); //将用户空间拷贝到内核空间的 status
    	if (ret < 0)
    		printk(KERN_DEBUG"write error!!!\n"); //输出信息
    	else
    		printk(KERN_DEBUG"write led ok!!!\n"); //输出信息
    	*((volatile size_t*)gpioe_data) &= ~(1 << 12);//清除 GPIOE12 状态
    	if (status)
    		*((volatile size_t*)gpioe_data) |= (1 << 12);//设置 GPIOE12 状态 1
    	return 0;
    }
    
    //指定模块入口和出口
    static int led_driver_init(void) 
    {
    	platform_driver_register(&led_driver);
    	return 0;
    }
    static void led_driver_exit(void)
    {
    	platform_driver_unregister(&led_driver);
    }
    
    module_init(led_driver_init);
    module_exit(led_driver_exit);
    
    MODULE_LICENSE("GPL"); //不加的话加载会有错误提醒
    MODULE_AUTHOR("1477153217@qq.com"); //作者
    MODULE_VERSION("0.1"); //版本
    MODULE_DESCRIPTION("led_driver"); //简单的描述
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    杂项字符设备msic
    • 有些设备可能并不属于字符设备、块设备、网络设备这三种,而是类似于字符设备
    • Linux 内核提供了一个新的设备类型,也就是 miscdevice。一般称该类型为杂项设备,
    • 但其实该设备广泛上说还是属于字符设备,只是该设备使用的结构体不是 cdev,而是 miscdevice 结构体。
    • 所有的 misc 设备的主设备号都是 10,因此这样我们不需要自己指定主设备号了
    • 在misc_register 函数中已经对设备进行了动态分配,同时调用了 class_create 和 device_create 实现了设备节点的自动创建
    • 其 misc_deregister 函数也是一样,其内部已经实现了设备节点的注销。
    /*misc设备的注册步骤*/
    /*(1)定义misc设备结构体*/
    static struct miscdevice misc_led_dev = {
    	.minor = MISC_DYNAMIC_MINOR,//动态分配次设备号
    	.name  = "miscled",//设备节点名,实际系统/dev目录下
    	.fops  = &miec_led_fops,//文件操作集
     }
    
    /*(2)定义miec_led_fop文件操作结构体及实现*/
    static int misc_led_close(struct inode* inode, struct file* filp)
    {
    	/*GPIOE_CONFIG*/
    	printk(KERN_DEBUG"close led!!\n");
    	return 0;
    }
    static int misc_led_read(struct file* filp, char __user* buff, size_t count, loff_t* offp)
    {
    	int ret;
    	size_t status = *((volatile size_t*)gpioe_data);//获取 GPIOE12 状态
    	ret = copy_to_user(buff, &status, 4);//将内核空间拷贝到用户空间 buff
    	if (ret < 0)
    		printk(KERN_DEBUG"read error!!!\n");
    	else
    		printk(KERN_DEBUG"read led ok!!!\n");
    	return 0;
    }
    static int misc_led_write(struct file* filp, const char __user* buff, size_t count,loff_t* offp)
    {
    	int ret;
    	size_t status;
    	ret = copy_from_user(&status, buff, 4);
    	if (ret < 4)
    		printk(KERN_DEBUG"read error!!!\n");
    	else
    		printk(KERN_DEBUG"read led ok!!!\n");
    	*((volatile size_t*)gpioe_data) &= ~(1 << 12);//设置 GPIOE12 状态 
    	if(status)
    		*((volatile size_t*)gpioe_data) &= (1 << 12);//设置 GPIOE12 状态 1
    	return 0;
    }
    static struct file_operations misc_led_ops = {
    	.owner = THIS_MODULE,
    	.open  = misc_led_open,
    	.read  = misc_led_read,
    	.write = misc_led_write,
    	.release = misc_led_close,
    };
    
    /*(3)注册misc设备(paltform模型实现设备的注册)*/
    static int misc_led_probe(struct platform_device* pdev)
    {
    	struct resource* res;//设备硬件资源(寄存器地址信息)
    	int ret;
    	misc_register(&misc_led_dev);//注册misc设备(包含了动态匹配+初始化+创建类与设备)
    	res = platform_get_resource(pdev,IORESOURCE_MEM,0);//获取 device 中的 GPIOE_CFG0
    	gpioe_cfg0 = ioremap(res->start,(res->end - res->start)+1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 1); //获取 device 中的 GPIOE_CFG1//获取 device 中的 GPIOE_CFG1
    	gpioe_cfg1 = ioremap(res->start, (res->end - res->start) + 1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 2); //获取 device 中的 GPIOE_DATA
    	gpioe_data = ioremap(res->start, (res->end - res->start) + 1);
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 3); //获取 device 中的 GPIOE_PUL0
    	gpioe_pul0 = ioremap(res->start, (res->end - res->start) + 1);
    	return 0;
    }
    
    /*(4)注销misc设备*/
    static int misc_led_remove(struct platform_device* pdev)
    {
    	iounmap(gpioe_cfg0);//取消 GPIOE_CFG0 映射
    	iounmap(gpioe_cfg1); //取消 GPIOE_CFG1 映射
    	iounmap(gpioe_data); //取消 GPIOE_DATA 映射
    	iounmap(gpioe_pul0); //取消 GPIOE_PUL0 映射
    	misc_deregister(&misc_led_dev);//注销 misc 设备
    	return 0;
    }
    
    /*(5)实现平台设备结构体*/
    static struct of_device_id misc_led_match_table[] = {
    	{.compatible = "lite200,misc_led",}/*一对多*/
    };
    static struct platform_device_id misc_led_device_ids[] = {
    	{.name="misc_led",},/*匹配*/
    };
    static struct platform_driver misc_led_driver = {
    	.probe = misc_led_probe,
    	.remove = misc_led_remove,
    	.driver = {
    		.name = "misc_led",
    		.of_match_table = misc_led_match_table,
    	},
    	.id_table = misc_led_device_ids,
    };
    
    /*(6)指定模块输入口以及出口*/
    static int misc_led_driver_init(void)
    {
    	platform_driver_register(&misc_led_driver);
    	return 0;
    }
    static void misc_led_driver_exit(void) {
    	platform_driver_unregister(&misc_led_driver);
    }
    module_init(misc_led_driver_init);
    module_exit(misc_led_driver_exit);\
    
    MODULE_LICENSE("GPL"); //不加的话加载会有错误提醒
    MODULE_AUTHOR("1477153217@qq.com"); //作者
    MODULE_VERSION("0.1"); //版本
    MODULE_DESCRIPTION("misc_led_driver"); //简单的描述
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
  • 相关阅读:
    学习笔记:机器学习之回归
    数据结构教程(第五版 李春葆 上机实验题4 验证性实验)
    【ICer的脚本练习】通过perl脚本来检查仿真log的结果
    使用外部时钟,通过TIM21_CH1,对STM32L0XX内部的RC时钟(HSI/MSI等)进行校准
    Java真的不难(四十六)Spring Boot的入门
    OSCP-Vulnhub靶机记录-GoldenEye-walkthrough
    【前端】Js
    简单理解函数f(x;θ)中分号的含义
    Kotlin高仿微信-第27篇-朋友圈-相册选择图片或小视频
    SpringMVC: Java Web应用开发的框架之选
  • 原文地址:https://blog.csdn.net/weixin_47397155/article/details/126385857