lv15 平台总线驱动开发——ID匹配 3

一、ID匹配之框架代码

id匹配(可想象成八字匹配):一个驱动可以对应多个设备 ------优先级次低(上一章名称匹配只能1对1

注意事项:

  1. device模块中,id的name成员必须与struct platform_device中的name成员内容一致,因此device模块中,struct platform_device中的name成员必须指定

  2. driver模块中,struct platform_driver成员driver的name成员必须指定,但与device模块中name可以不相同

1.1 用法展示:

/*platform device框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
​
//定义资源数组
​
static void device_release(struct device *dev)
{printk("platform: device release\n");
}
​
struct platform_device_id test_id = {   //<-------------.name = "test_device",   
};
​
struct platform_device test_device = {.name = "test_device",//必须初始化  //<------------必须一样.dev.release = device_release, .id_entry = &test_id,
};
​
static int __init platform_device_init(void)
{platform_device_register(&test_device);return 0;
}
​
static void __exit platform_device_exit(void)
{platform_device_unregister(&test_device);
}
​
module_init(platform_device_init);
module_exit(platform_device_exit);
MODULE_LICENSE("Dual BSD/GPL");

/*platform driver框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
​
static int driver_probe(struct platform_device *dev)
{printk("platform: match ok!\n");return 0;
}
​
static int driver_remove(struct platform_device *dev)
{printk("platform: driver remove\n");return 0;
}
​
struct platform_device_id testdrv_ids[] =     //<---------------
{[0] = {.name = "test_device"},          //1对多匹配[1] = {.name = "abcxyz"},               //1对多匹配[2] = {}, //means ending                //结束符
};
​
struct platform_driver test_driver = {.probe = driver_probe,.remove = driver_remove,.driver = {.name = "xxxxx", //必须初始化 可以与device不一样},.id_table = testdrv_ids,   //<------------
};
​
static int __init platform_driver_init(void)
{platform_driver_register(&test_driver);return 0;
}
​
static void __exit platform_driver_exit(void)
{platform_driver_unregister(&test_driver);
}
​
module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("Dual BSD/GPL");
​

用到结构体数组,一般不指定大小,初始化时最后加{}表示数组结束

设备中增加资源,驱动中访问资源

1.2 test_device和test_driver示例改写

test_device_id.c

/*platform device框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>//<---------------设计成数组形式
struct resource test_dev_res [] =
{[0] = {.start = 0x1000, .end = 0x1003, .name = "reg1", .flags = IORESOURCE_MEM},[1] = {.start = 0x2000, .end = 0x2003, .name = "reg2", .flags = IORESOURCE_MEM},[2] = {.start = 10, .end = 10, .name = "irq1", .flags = IORESOURCE_IRQ},            //中断号10[3] = {.start = 0x3000, .end = 0x3003, .name = "reg3", .flags = IORESOURCE_MEM},[4] = {.start = 100, .end = 100, .name = "irq2", .flags = IORESOURCE_IRQ},[5] = {.start = 62, .end = 62, .name = "irq3", .flags = IORESOURCE_IRQ},
};//定义资源数组static void device_release(struct device *dev)
{printk("platform: device release\n");
}struct platform_device_id test_id = {         //<--------------- .name = "test_device",
};struct platform_device test_device = {.id_entry = &test_id,                   //<--------------- .name = "test_device",//必须初始化.dev.release = device_release,  .resource = test_dev_res,                      .num_resources = ARRAY_SIZE(test_dev_res),    
};static int __init platform_device_init(void)
{platform_device_register(&test_device);return 0;
}static void __exit platform_device_exit(void)
{platform_device_unregister(&test_device);
}module_init(platform_device_init);
module_exit(platform_device_exit);
MODULE_LICENSE("Dual BSD/GPL");

test_driver_id.c

/*platform driver框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>static int driver_probe(struct platform_device *dev)
{struct resource * pres = NULL;                       printk("platform: match ok!\n");pres = platform_get_resource(dev,IORESOURCE_MEM,2);   printk("res.start = 0x%x\n",(unsigned int)pres->start);pres = platform_get_resource(dev,IORESOURCE_IRQ,1);   printk("res.start = %d\n",(int)pres->start);return 0;
}static int driver_remove(struct platform_device *dev)
{printk("platform: driver remove\n");return 0;
}struct platform_device_id testdrv_ids[] =          //<---------------
{[0] = {.name = "test_device"},[1] = {.name = "xyz"},[2] = {},          //表示结束符
};struct platform_driver test_driver = {.probe = driver_probe,.remove = driver_remove,.driver = {.name = "abc", //必须初始化         //<------名字故意与device不一样},.id_table = testdrv_ids,  //<--------------- 
};static int __init platform_driver_init(void)
{platform_driver_register(&test_driver);return 0;
}static void __exit platform_driver_exit(void)
{platform_driver_unregister(&test_driver);
}module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("Dual BSD/GPL");

Makefile

ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_installclean:rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versionselseCONFIG_MODULE_SIG=n
#obj-m += fs4412_led_device.o
#obj-m += fs4412_led_driver.o
obj-m += test_driver_id.o
obj-m += test_device_id.oendif

编译测试

 

二、ID匹配之led驱动

改写fs4412_led_device_idmatch.c

/*platform device框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>#include "leddrv.h"#define GPX1CON 0x11000C20
#define GPX1DAT 0x11000C24#define GPX2CON 0x11000C40
#define GPX2DAT 0x11000C44#define GPF3CON 0x114001E0
#define GPF3DAT 0x114001E4//<---------------修改寄存器地址
struct resource fs4412led_dev_res [] =
{[0] = {.start = GPX1CON, .end = GPX1CON+3, .name = "GPX1CON", .flags = IORESOURCE_MEM},[1] = {.start = GPX1DAT, .end = GPX1DAT+3, .name = "GPX1DAT", .flags = IORESOURCE_MEM},[2] = {.start = GPX2CON, .end = GPX2CON+3, .name = "GPX2CON", .flags = IORESOURCE_MEM},[3] = {.start = GPX2DAT, .end = GPX2DAT+3, .name = "GPX2DAT", .flags = IORESOURCE_MEM},[4] = {.start = GPF3CON, .end = GPF3CON+3, .name = "GPF3CON", .flags = IORESOURCE_MEM},[5] = {.start = GPF3DAT, .end = GPF3DAT+3, .name = "GPF3DAT", .flags = IORESOURCE_MEM},
};//定义资源数组static void fs4412led_dev_release(struct device *dev)
{printk("platform: fs4412led_dev_release is called\n");
}                         struct platform_device_id fs4412led_id = {     //<----------.name = "fs4412led",
};                    struct platform_device fs4412led_device = {.id_entry = &fs4412led_id,                   //<-------------.name = "fs4412led",//必须初始化.dev.release = fs4412led_dev_release,  .resource = fs4412led_dev_res,                     .num_resources = ARRAY_SIZE(fs4412led_dev_res),     
};static int __init fs4412led_dev_init(void)
{platform_device_register(&fs4412led_device);return 0;
}static void __exit fs4412led_dev_exit(void)
{platform_device_unregister(&fs4412led_device);
}module_init(fs4412led_dev_init);
module_exit(fs4412led_dev_exit);
MODULE_LICENSE("Dual BSD/GPL");

改写改写fs4412_led_driver_idmatch.c

/*platform driver框架*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>#include "leddrv.h"int major = 11;
int minor = 0;
int myled_num  = 1;struct myled_dev
{struct cdev mydev;volatile unsigned long *pled2_con;  volatile unsigned long *pled2_dat;volatile unsigned long *pled3_con;volatile unsigned long *pled3_dat;volatile unsigned long *pled4_con;volatile unsigned long *pled4_dat;volatile unsigned long *pled5_con;volatile unsigned long *pled5_dat;
/*
volatile 防止优化。对这块指针指向的内存,有时候cpu会把外设寄存器中的值读到内部寄存器中,方便下次读的时候更快.加了voltatile就不会优化,否则cpu可能会从内部寄存器中读取,而不是去外设寄存器中读取。
*/struct class *cls;       //<-----------------struct device *dvs;       //<-----------------};struct myled_dev *pgmydev = NULL;int myled_open(struct inode *pnode,struct file *pfile)
{pfile->private_data =(void *) (container_of(pnode->i_cdev,struct myled_dev,mydev));return 0;
}int myled_close(struct inode *pnode,struct file *pfile)
{return 0;
}void led_on(struct myled_dev *pmydev,int ledno)
{switch(ledno){case 2:writel(readl(pmydev->pled2_dat) | (0x1 << 7),pmydev->pled2_dat);break;case 3:writel(readl(pmydev->pled3_dat) | (0x1),pmydev->pled3_dat);break;case 4:writel(readl(pmydev->pled4_dat) | (0x1 << 4),pmydev->pled4_dat);break;case 5:writel(readl(pmydev->pled5_dat) | (0x1 << 5),pmydev->pled5_dat);break;}
}void led_off(struct myled_dev *pmydev,int ledno)
{switch(ledno){case 2:writel(readl(pmydev->pled2_dat) & (~(0x1 << 7)),pmydev->pled2_dat);break;case 3:writel(readl(pmydev->pled3_dat) & (~(0x1)),pmydev->pled3_dat);break;case 4:writel(readl(pmydev->pled4_dat) & (~(0x1 << 4)),pmydev->pled4_dat);break;case 5:writel(readl(pmydev->pled5_dat) & (~(0x1 << 5)),pmydev->pled5_dat);break;}
}long myled_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{struct myled_dev *pmydev = (struct myled_dev *)pfile->private_data;if(arg < 2 || arg > 5){return -1;}switch(cmd){case MY_LED_ON:led_on(pmydev,arg);break;case MY_LED_OFF:led_off(pmydev,arg);break;default:return -1;}return 0;
}void ioremap_ledreg(struct myled_dev *pmydev,struct platform_device *p_pltdev)
{struct resource *pres = NULL;pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,2);pmydev->pled2_con = ioremap(pres->start,4);pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,3);pmydev->pled2_dat = ioremap(pres->start,4);pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,0);pmydev->pled3_con = ioremap(pres->start,4);pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,1);pmydev->pled3_dat = ioremap(pres->start,4);pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,4);pmydev->pled4_con = ioremap(pres->start,4);pres = platform_get_resource(p_pltdev,IORESOURCE_MEM,5);pmydev->pled4_dat = ioremap(pres->start,4);pmydev->pled5_con = pmydev->pled4_con;pmydev->pled5_dat = pmydev->pled4_dat;
}void iounmap_ledreg(struct myled_dev *pmydev)
{iounmap(pmydev->pled2_con);pmydev->pled2_con = NULL;iounmap(pmydev->pled2_dat);pmydev->pled2_dat = NULL;iounmap(pmydev->pled3_con);pmydev->pled3_con = NULL;iounmap(pmydev->pled3_dat);pmydev->pled3_dat = NULL;iounmap(pmydev->pled4_con);pmydev->pled4_con = NULL;iounmap(pmydev->pled4_dat);pmydev->pled4_dat = NULL;pmydev->pled5_con = NULL;pmydev->pled5_dat = NULL;
}void set_output_ledconreg(struct myled_dev *pmydev)
{writel((readl(pmydev->pled2_con) & (~(0xF << 28))) | (0x1 << 28),pmydev->pled2_con);writel((readl(pmydev->pled3_con) & (~(0xF))) | (0x1),pmydev->pled3_con);writel((readl(pmydev->pled4_con) & (~(0xF << 16))) | (0x1 << 16),pmydev->pled4_con);writel((readl(pmydev->pled5_con) & (~(0xF << 20))) | (0x1 << 20),pmydev->pled5_con);writel(readl(pmydev->pled2_dat) & (~(0x1 << 7)),pmydev->pled2_dat);writel(readl(pmydev->pled3_dat) & (~(0x1)),pmydev->pled3_dat);writel(readl(pmydev->pled4_dat) & (~(0x1 << 4)),pmydev->pled4_dat);writel(readl(pmydev->pled5_dat) & (~(0x1 << 5)),pmydev->pled5_dat);
}struct file_operations myops = {.owner = THIS_MODULE,.open = myled_open,.release = myled_close,.unlocked_ioctl = myled_ioctl,
};static int fs4412led_driver_probe(struct platform_device *p_pltdev)
{int ret = 0;dev_t devno = MKDEV(major,minor);/*申请设备号*/ret = register_chrdev_region(devno,myled_num,"myled");if(ret){ret = alloc_chrdev_region(&devno,minor,myled_num,"myled");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}pgmydev = (struct myled_dev *)kmalloc(sizeof(struct myled_dev),GFP_KERNEL);if(NULL == pgmydev){unregister_chrdev_region(devno,myled_num);printk("kmalloc failed\n");return -1;}memset(pgmydev,0,sizeof(struct myled_dev)); //这里的memset并非c库的函数,而是内核自己实现的memset函数/*给struct cdev对象指定操作函数集*/	cdev_init(&pgmydev->mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/pgmydev->mydev.owner = THIS_MODULE;cdev_add(&pgmydev->mydev,devno,myled_num);/*ioremap*/ioremap_ledreg(pgmydev,p_pltdev);/*con-register set output*/set_output_ledconreg(pgmydev);pgmydev->cls = class_create(THIS_MODULE, "myled");    if(IS_ERR(pgmydev->cls)){printk("class_create failed\n");cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,myled_num);return -1;}pgmydev->dvs = device_create(pgmydev->cls, NULL, devno, NULL,"myled");   if(pgmydev->dvs == NULL){printk("device_create failed\n");class_destroy(pgmydev->cls);cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,myled_num);return -1;}return 0;
}static int fs4412led_driver_remove(struct platform_device *dev)
{dev_t devno = MKDEV(major,minor);/*iounmap*/iounmap_ledreg(pgmydev);cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,myled_num);kfree(pgmydev);pgmydev = NULL;printk("platform: driver remove\n");return 0;
}struct platform_device_id fs4412led_ids[] =  //<---------------------
{[0] = {.name = "fs4412led"},            [1] = {.name = "xyz"},[2] = {},
};struct platform_driver fs4412led_driver = {.probe = fs4412led_driver_probe,.remove = fs4412led_driver_remove,.driver = {.name = "abc", //必须初始化},.id_table = fs4412led_ids, //<-------------------------
};static int __init fs4412led_driver_init(void)
{platform_driver_register(&fs4412led_driver);return 0;
}static void __exit fs4412led_driver_exit(void)
{platform_driver_unregister(&fs4412led_driver);return;
}module_init(fs4412led_driver_init);
module_exit(fs4412led_driver_exit);
MODULE_LICENSE("Dual BSD/GPL");

改写Makefile

ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_installclean:rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versionselseCONFIG_MODULE_SIG=n
obj-m += fs4412_led_device_idmatch.o
obj-m += fs4412_led_driver_idmatch.oendif

 编译测试

三、设备树匹配

设备树匹配:内核启动时根据设备树自动产生的设备 ------ 优先级最高(大部分设备的方法)

注意事项:

  1. 无需编写device模块,只需编写driver模块

  2. 使用compatible属性进行匹配,注意设备树中compatible属性值不要包含空白字符(空格tab键不可以有)

  3. id_table可不设置,但struct platform_driver成员driver的name成员必须设置

  4. 可以1对多

/*platform driver框架*/
#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
​
static int driver_probe(struct platform_device *dev)
{printk("platform: match ok!\n");return 0;
}
​
static int driver_remove(struct platform_device *dev)
{printk("platform: driver remove\n");return 0;
}
​
struct platform_device_id testdrv_ids[] = 
{[0] = {.name = "test_device"},[1] = {.name = "abcxyz"},[2] = {}, //means ending
};
​
struct of_device_id test_of_ids[] =   //<---------------------也可以1对多
{[0] = {.compatible = "xyz,abc"},[1] = {.compatible = "qwe,opq"},[2] = {},
};
​
struct platform_driver test_driver = {.probe = driver_probe,.remove = driver_remove,.driver = {.name = "xxxxx", //必须初始化.of_match_table = test_of_ids,   //<---------------------},
};
​
static int __init platform_driver_init(void)
{platform_driver_register(&test_driver);return 0;
}
​
static void __exit platform_driver_exit(void)
{platform_driver_unregister(&test_driver);
}
​
module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("Dual BSD/GPL");

四、设备树匹配之led驱动

改写leddrv_dt.c为fs4412_led_driver_treecmatch.c

改写重点创建struct platform_driver结构体,把init和exit改为probe和remove方式

j

这个成员匹配成功后的pnode成员

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>#include "leddrv.h"int major = 11;
int minor = 0;
int myled_num  = 1;//不会对寄存器直接操作,改为对设备编号操作
struct myled_dev
{struct cdev mydev;unsigned int led2gpio;unsigned int led3gpio;unsigned int led4gpio;unsigned int led5gpio;
};struct myled_dev *pgmydev = NULL;int myled_open(struct inode *pnode,struct file *pfile)
{pfile->private_data =(void *) (container_of(pnode->i_cdev,struct myled_dev,mydev));return 0;
}int myled_close(struct inode *pnode,struct file *pfile)
{return 0;
}void led_on(struct myled_dev *pmydev,int ledno)
{switch(ledno){case 2:gpio_set_value(pmydev->led2gpio,1);break;case 3:gpio_set_value(pmydev->led3gpio,1);break;case 4:gpio_set_value(pmydev->led4gpio,1);break;case 5:gpio_set_value(pmydev->led5gpio,1);break;}
}void led_off(struct myled_dev *pmydev,int ledno)
{switch(ledno){case 2:gpio_set_value(pmydev->led2gpio,0);break;case 3:gpio_set_value(pmydev->led3gpio,0);break;case 4:gpio_set_value(pmydev->led4gpio,0);break;case 5:gpio_set_value(pmydev->led5gpio,0);break;}
}long myled_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
{struct myled_dev *pmydev = (struct myled_dev *)pfile->private_data;if(arg < 2 || arg > 5){return -1;}switch(cmd){case MY_LED_ON:led_on(pmydev,arg);break;case MY_LED_OFF:led_off(pmydev,arg);break;default:return -1;}return 0;
}struct file_operations myops = {.owner = THIS_MODULE,.open = myled_open,.release = myled_close,.unlocked_ioctl = myled_ioctl,
};//申请gpio编号,init中会调用
void request_leds_gpio(struct myled_dev *pmydev,struct device_node *pnode)
{pmydev->led2gpio = of_get_named_gpio(pnode,"led2-gpio",0);gpio_request(pmydev->led2gpio,"led2");pmydev->led3gpio = of_get_named_gpio(pnode,"led3-gpio",0);gpio_request(pmydev->led3gpio,"led3");pmydev->led4gpio = of_get_named_gpio(pnode,"led4-gpio",0);gpio_request(pmydev->led4gpio,"led4");pmydev->led5gpio = of_get_named_gpio(pnode,"led5-gpio",0);gpio_request(pmydev->led5gpio,"led5");
}void set_leds_gpio_output(struct myled_dev *pmydev)
{gpio_direction_output(pmydev->led2gpio,0);gpio_direction_output(pmydev->led3gpio,0);gpio_direction_output(pmydev->led4gpio,0);gpio_direction_output(pmydev->led5gpio,0);
}void free_leds_gpio(struct myled_dev *pmydev)
{gpio_free(pmydev->led2gpio);gpio_free(pmydev->led3gpio);gpio_free(pmydev->led4gpio);gpio_free(pmydev->led5gpio);
}int fs4412led_driver_probe(struct platform_device *p_pltdev)
{int ret = 0;dev_t devno = MKDEV(major,minor);struct device_node *pnode = NULL;//	pnode = of_find_node_by_path("/fs4412-leds");
//	if(NULL == pnode)
//	{
//		printk("find node by path failed\n");
//		return -1;
//	}pnode = p_pltdev->dev.of_node;   //<---------------------------修改为此种方式获取pnode/*申请设备号*/ret = register_chrdev_region(devno,myled_num,"myled");if(ret){ret = alloc_chrdev_region(&devno,minor,myled_num,"myled");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}pgmydev = (struct myled_dev *)kmalloc(sizeof(struct myled_dev),GFP_KERNEL);if(NULL == pgmydev){unregister_chrdev_region(devno,myled_num);printk("kmalloc failed\n");return -1;}memset(pgmydev,0,sizeof(struct myled_dev)); //这里的memset并非c库的函数,而是内核自己实现的memset函数/*给struct cdev对象指定操作函数集*/	cdev_init(&pgmydev->mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/pgmydev->mydev.owner = THIS_MODULE;cdev_add(&pgmydev->mydev,devno,myled_num);/*ioremap*/request_leds_gpio(pgmydev,pnode);/*con-register set output*/set_leds_gpio_output(pgmydev);return 0;
}void fs4412led_driver_remove(struct platform_device *p_pltdev)
{dev_t devno = MKDEV(major,minor);/*iounmap*/free_leds_gpio(pgmydev);cdev_del(&pgmydev->mydev);unregister_chrdev_region(devno,myled_num);kfree(pgmydev);pgmydev = NULL;
}struct of_device_id fs4412_of_ids[]=    //<--------------------
{[0] = {.compatible = "fs4412,led2-5"},  //<-----------------[1] = {.compatible = "qwe,led6-10"},[2] = {},
};struct platform_driver fs4412led_driver = 
{.probe = fs4412led_driver_probe,.remove = fs4412led_driver_remove,.driver = {.name = "abcdef",.of_match_table = fs4412_of_ids, //<-----------------------},
};int __init myled_init(void)
{platform_driver_register(&fs4412led_driver);return 0;
}void __exit myled_exit(void)
{platform_driver_unregister(&fs4412led_driver);return;
}MODULE_LICENSE("GPL");module_init(myled_init);
module_exit(myled_exit);

改写Makefie

ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_installclean:rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versionselseCONFIG_MODULE_SIG=n
#obj-m += fs4412_led_device_idmatch.o
#obj-m += fs4412_led_driver_idmatch.o
obj-m += fs4412_led_driver_treematch.oendif

测试 

五、一个编写驱动用的宏

熟悉这样的写法,等同于init和exit

struct platform_driver xxx = {  ...
};
module_platform_driver(xxx);
//最终展开后就是如下形式:
static int __init xxx_init(void)
{return platform_driver_register(&xxx);
}
module_init(xxx_init);
static void __exit xxx_init(void)
{return platform_driver_unregister(&xxx);
}
module_exit(xxx_exit)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/257246.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL主从环境,主库改端口后,从库如何操作?

主库&#xff1a;mysql-111 从库&#xff1a;mysql-112 主库由3306端口修改成3307后&#xff0c; 从库执行如下命令 mysql> stop slave; mysql> change master to master_port3307; mysql> CHANGE MASTER TO MASTER_HOST192.168.10.111,MASTER_USERbeifen,MASTER_PA…

npm install 安装依赖如何加速

在使用npm安装依赖时&#xff0c;有几种方法可以加速这一过程&#xff0c;尤其是在面临网络限制或npm官方源速度慢的情况下。以下是一些常用的加速技巧&#xff1a; 1. 使用国内镜像源 国内有几个镜像源可以提供更快的下载速度&#xff0c;例如淘宝npm镜像。你可以通过以下命…

如何用一根网线和51单片机做简单门禁[带破解器]

仓库:https://github.com/MartinxMax/Simple_Door 支持原创是您给我的最大动力… 原理 -基础设备代码程序- -Arduino爆破器程序 or 51爆破器程序- 任意选一个都可以用… —Arduino带TFT屏幕——— —51带LCD1602——— 基础设备的最大密码长度是0x7F&#xff0c;因为有一位…

Excel模板1:彩色甘特图

Excel模板1&#xff1a;彩色甘特图 分享地址 当前效果&#xff1a;只需要填写进度&#xff0c; 其余效果都是自动完成的 。 阿里网盘永久分享&#xff1a;https://www.alipan.com/s/cXhq1PNJfdm ​省心。能用公式的绝不使用手动输入。 ​​ 这个区域以及标题可以手动输入…

Python爬虫——解析库安装(1)

目录 1.lxml安装2.Beautiful Soup安装3.pyquery 的安装 我创建了一个社区&#xff0c;欢迎大家一起学习交流。社区名称&#xff1a;Spider学习交流 注&#xff1a;该系列教程已经默认用户安装了Pycharm和Anaconda&#xff0c;未安装的可以参考我之前的博客有将如何安装。同时默…

机器学习之局部最优和全局最优

(1)局部最优&#xff0c;就是在函数值空间的一个有限区域内寻找最小值;而全局最优&#xff0c;是在函数值空间整个区域寻找最小值问题。 (2)函数局部最小点是它的函数值小于或等于附近点的点&#xff0c;但是有可能大于较远距离的点。 (3)全局最小点是那种它的函数值小于或等于…

二叉树-------前,中,后序遍历 + 前,中,后序查找+删除节点 (java详解)

目录 提要&#xff1a; 创建一个简单的二叉树&#xff1a; 二叉树的前中后序遍历&#xff1a; 二叉树的前序遍历&#xff1a; 二叉树的中序遍历&#xff1a; 二叉树的后续遍历&#xff1a; 小结&#xff1a; 二叉树的前中后续查找&#xff1a; 二叉树的前序查找&#…

Python 数据可视化之山脊线图 Ridgeline Plots

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 JoyPy 是一个基于 matplotlib pandas 的单功能 Python 包&#xff0c;它的唯一目的是绘制山脊线图 Joyplots&#xff08;也称为 Ridgeline Plots&…

SpringMVC原理(设计原理+启动原理+工作原理)

文章目录 前言正文一、设计原理1.1 servlet生命周期简述1.2 设计原理小结 二、启动原理2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析2.2 DispatcherServlet 的初始化2.3 DispatcherServlet#initHandlerMappings(...) 初始化示例说明 三、工作原理 前言 …

python - OSError:错误没有名为 [‘pytorch_model.bin‘

python - OSError&#xff1a;错误没有名为 [‘pytorch_model.bin’] 自己训练的模型存储好了以后 model MT5ForConditionalGeneration.from_pretrained(“ner/best”) 之前还可以跑 现在报错 错误没有名为 [‘pytorch_model.bin’] 还原了一下conda env 把四版变成三版了 …

面试前的准备

面试前的准备 Java程序员校招与社招的区别 校招和社招都是企业招聘形式的一种&#xff0c;只是面向的对象不同。校招 只允许在校生参加&#xff0c;社招理论上是任何人都能参加的(包括在校生)。 但是&#xff0c;无论是社招还是校招&#xff0c;它的难度都取决于你的水平高低。…

Unity SRP 管线【第十讲:SRP/URP 图形API】

Unity 封装的图形API 文章目录 Unity 封装的图形API一、 CommandBuffer 要执行的图形命令列表1. CommandBuffer 属性2. CommandBuffer 常用图形API&#xff08;方法&#xff09;(1)设置(2)获取临时纹理 GetTemporaryRT以及释放(3)设置纹理为渲染目标 SetRenderTarget(4)Command…

每日OJ题_递归①_力扣面试题 08.06. 汉诺塔问题

目录 递归算法原理 力扣面试题 08.06. 汉诺塔问题 解析代码 递归算法原理 递归算法个人经验&#xff1a;给定一个任务&#xff0c;相信递归函数一定能解决这个任务&#xff0c;根据任务所需的东西&#xff0c;给出函数参数&#xff0c;然后实现函数内容&#xff0c;最后找出…

Shell 学习笔记(三)-shell变量

Shell 语言是一种动态类型和弱类型语言, 因此,在Shell中无需显示地声明变量, 且变量的类型会根据不同的操作符而发生变化. 静态类型语言: 在程序编译期间就确定变量类型的语言, 如java, C等 动态类型语言: 在程序运行期间才确定变量类型的语言, 如PHP, Python等. 一 shell变量…

Matplotlib初探:认识数据可视化与Matplotlib

Matplotlib初探&#xff1a;认识数据可视化与Matplotlib Fig.1 利用Matplotlib进行数据可视化( 可视化代码见文末) &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;一、数据可视化简介&#x1f333;&#x1f333;二、Matplotlib库简介&#x…

【数学建模】【2024年】【第40届】【MCM/ICM】【B题 搜寻潜水器】【解题思路】

一、题目 &#xff08;一&#xff09;赛题原文 2024 MCM Problem A: Resource Availability and Sex Ratios Maritime Cruises Mini-Submarines (MCMS), a company based in Greece, builds submersibles capable of carrying humans to the deepest parts of the ocean. A …

WordPress每天发布60s插件

源码名称:WordPress每天发布60s插件 适用平台:WordPress Wordpress还是比较适合个人博客网站&#xff0c;这个60秒插件适合一些喜欢自动发新闻早报晚报人员。 wordpress一直以来都是建立个人博客网站比较适合的脚手架&#xff0c;非常合适个人使用。 小程序源码就找 源码软件…

【Java数据结构】ArrayList和LinkedList的遍历

一&#xff1a;ArrayList的遍历 import java.util.ArrayList; import java.util.Iterator; import java.util.List;/*** ArrayList的遍历*/ public class Test {public static void main(String[] args) {List<Integer> list new ArrayList<>();list.add(5);list…

如何将 Hexo 部署到 GitHub Pages

引言 在数字时代&#xff0c;拥有个人博客是展示自己想法、分享知识和技能的绝佳方式。Hexo 是一个基于 Node.js 的静态博客生成器&#xff0c;它结合了简洁性和功能性&#xff0c;让我们可以轻松地建立并维护一个博客。而 GitHub Pages 提供了一个免费的平台来托管这些静态网站…

Stable Diffusion主流UI详细介绍

Stable Diffusion目前主流的操作界面有WebUI、ComfyUI以及Fooocus 这里webui和fooocus在人机交互上的逻辑是一样的&#xff0c;fooocus界面更加简洁。 comfyui是在人机交互上是采用流程节点的交互逻辑&#xff0c;和上面略有区别。 界面分别如下&#xff1a; WebUI界面如下 we…