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.hqwc.cn/news/468795.html

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

相关文章

Java加密秘钥

1. Java加密秘钥 您可加密信息,使其避免未经授权的访问。您可通过Java密码技术对数据进行编码来加密。密码技术包含各种用于对网络上发送的数据进行编码和译码的加密秘钥。Java密码架构(JCA)和Java密码扩展(JCE)可让您在Java平台上执行密码。 Java加密秘钥是一连串控制数…

Hive调优——合并小文件

目录 一、小文件产生的原因 二、小文件的危害 三、小文件的解决方案 3.1 小文件的预防 3.1.1 减少Map数量 3.1.2 减少Reduce的数量 3.2 已存在的小文件合并 3.2.1 方式一&#xff1a;insert overwrite (推荐) 3.2.2 方式二&#xff1a;concatenate 3.2.3 方式三&#xff…

【大数据Hive】hive 表设计常用优化策略

目录 一、前言 二、hive 普通表查询原理 2.1 操作演示说明 2.1.1 创建一张表&#xff0c;并加载数据 2.1.2 统计3月24号的登录人数 2.1.3 查询原理过程总结 2.2 普通表结构带来的问题 三、hive分区表设计 3.1 区表结构 - 分区设计思想 3.2 操作演示 3.2.1 创建分区表…

x86汇编通用寄存器用途一览

文章目录 写在前面通用寄存器参考资料 写在前面 intel官方文档链接&#xff1a;Intel64和IA-32架构软件开发者手册 具体在Combined Volume Set of Intel 64 and IA-32 Architectures Software Developer’s Manuals这本手册 &#xff08;五千页我的天。。。&#xff09; 不想…

【C++初阶:类和对象(下篇)】初始化列表 | static成员 | 友元

目录 一、构造函数构造函数体赋值&#x1f43e;初始化列表&#x1f43e;&#x1f4a6; explicit关键字 二、static成员&#x1f43e;概念**&#x1f4a6; 关于静态的特性** 三、友元&#x1f4a6; **友元函数**&#x1f4a6; **友元类** **四、内部类** 一、构造函数 构造函数…

【教学类-47-01】20240206UIBOT+IDM下载儿童古诗+修改文件名

背景需求&#xff1a; 去年12月&#xff0c;我去了其他幼儿园参观&#xff0c;这是一个传统文化德育教育特色的学校&#xff0c;在“古典集市”展示活动中&#xff0c;小班中班大班孩子共同现场念诵《元日》《静夜思》包含了演唱版本和儿歌念诵版本。 我马上也要当班主任了&a…

在微信视频号上发表视频

我们手机打开微信 然后 最下面选择 发现 然后点击 上面的视频号 进入后 点击 右上角头像图标 然后 进入个人管理界面 左下角选择 发表视频 然后 进入一个录制界面 我们左下角 点击这个 从相册选择 打开相册后 选择自己需要的视频 然后 点击右下角下一步 觉得内容没问题 就…

进阶C语言-动态内存管理

动态内存管理 &#x1f388;1.为什么存在动态内存分配&#x1f388;2.动态内存函数的介绍&#x1f52d;2.1malloc和free函数&#x1f52d;2.2calloc函数&#x1f52d;2.3realloc函数 &#x1f388;3.常见的动态内存错误&#x1f52d;3.1对NULL指针的解引用操作&#x1f52d;3.2…

车载软件架构 —— Adaptive AUTOSAR软件架构

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师&#xff08;Wechat&#xff1a;gongkenan2013&#xff09;。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 本就是小人物&#xff0c;输了就是输了&#…

安卓价值1-如何在电脑上运行ADB

ADB&#xff08;Android Debug Bridge&#xff09;是Android平台的调试工具&#xff0c;它是一个命令行工具&#xff0c;用于与连接到计算机的Android设备进行通信和控制。ADB提供了一系列命令&#xff0c;允许开发人员执行各种操作&#xff0c;包括但不限于&#xff1a; 1. 安…

C++中的析构函数

一、析构函数概念 析构函数不是完成对象的销毁&#xff0c;对象的销毁是由编译器完成的。析构函数完成的是对象中资源的清理工作。通常是对对象中动态开辟的空间进行清理。 二、析构函数特性 1.析构函数的函数名是 ~类名 2.析构函数无参数无返回值 3.一个类中只能有一个析…

Unity(单元测试)在STM32上的移植与应用

概述 Unity Test是一个为C构建的单元测试框架。本文基于STM32F407为基础&#xff0c;完全使用STM32CubeIDE进行开发&#xff0c;移植和简单使用Unity。 单片机型号&#xff1a;STM32F407VET6 软件&#xff1a;STM32CubeIDE Version: 1.14.1 Unity Version&#xff1a;2.…