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.mzph.cn/news/682553.shtml

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

相关文章

猫头虎分享已解决Bug || ValueError: No gradients provided for any variable

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

2019年通信工程师初级 实务 真题

文章目录 一、第9章 通信动力与环境通信电源系统的主要功能&#xff1a;“供”、“配”、“储”、“发”、“变” 二、第2章 传输网三、第3章 接入网四、第4章 互联网 一、第9章 通信动力与环境 【问题一】 网络通信设备对动力与环境的质量要求可以归纳为 &#xff08;1&#…

计算机服务器中了360后缀勒索病毒怎么办?360后缀勒索病毒处理流程

网络技术的不断应用与发展&#xff0c;为企业的生产运营提供了有利保障&#xff0c;越来越多的企业走向数字化办公模式&#xff0c;并且企业的发展离不开数据支撑&#xff0c;重视数据安全成为了众多企业关心的主要话题。春节前后&#xff0c;云天数据恢复中心接到很多企业的求…

C++:IO流

目录 关于CIO流 C/C中的日期输入 连续输入的问题 C文件IO流 运算符>>的运用 二进制读写 文本读写 stringstream 关于CIO流 C系统中ios为基类&#xff0c;其他类都是直接或间接派生自ios类 C标准库提供了4个全局流对象cin、cout、cerr、clog (在使用时候必须要包…

LeetCode刷题计划

LeetCode刷题计划 推荐 代码随想录&#xff1a;https://github.com/youngyangyang04/leetcode-master 卡码网 练习ACM模式 https://kamacoder.com/ 01 #include <iostream> using namespace std;int main() {int a ,b;while(cin>>a>>b){cout<<ab<…

【AI视野·今日CV 计算机视觉论文速览 第293期】Fri, 19 Jan 2024

AI视野今日CS.CV 计算机视觉论文速览 Fri, 19 Jan 2024 Totally 103 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers ParaHome: Parameterizing Everyday Home Activities Towards 3D Generative Modeling of Human-Object Interactions Aut…

PyTorch深度学习快速入门教程 - 【小土堆学习笔记】

小土堆Pytorch视频教程链接 声明&#xff1a; 博主本人技术力不高&#xff0c;这篇博客可能会因为个人水平问题出现一些错误&#xff0c;但作为小白&#xff0c;还是希望能写下一些碰到的坑&#xff0c;尽力帮到其他小白 1 环境配置 1.1 pycharm pycharm建议使用2020的&…

petalinux2018.3安装步骤

1、虚拟机安装ubuntu-16.04.7-desktop-amd64.iso &#xff08;注意&#xff1a;安装ubuntu-18.04.6-desktop-amd64.iso和ubuntu-16.04.6-desktop-i386.iso会报以下错误&#xff09; environment: line 314: ((: 10 #15~1 > 10 #3: syntax error in expression (error toke…

Excel

1、Excel的学习路径 2、掌握excel的基础要求 01、保证新版本 02、培养好的数据表格习惯 03、主动性探索 04、多联系 一、函数 二、文本清洗函数 三、常见文本的清洗函数 获取k的位置 FIND("k",P2,1) 从第1个位置开始在位置P2&#xff0c;查询字段k&#x…

Java+SpringBoot+Vue:高校科研管理的技术革新

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

ChatGPT高效提问—prompt实践(智能开发)

ChatGPT高效提问—prompt实践&#xff08;智能开发&#xff09; ​ 如果你是一名程序员&#xff0c;一定有过这样的经历&#xff1a;排查某个bug话费好几个小时&#xff0c;在各个搜索引擎尝试搜索&#xff0c;浏览了几十甚至上百个网站&#xff0c;结果搜到的答案千篇一律&am…

websocket简易基操

一、概述 1.1 简介 WebSocket是HTML5下一种新的协议&#xff08;websocket协议本质上是一个基于tcp的协议&#xff09;&#xff0c;它实现了浏览器与服务器全双工通信&#xff0c;能更好的节省服务器资源和带宽并达到实时通讯的目的&#xff0c;Websocket是一个持久化的协议。…

【STM32 CubeMX】GPIO的工作模式

文章目录 前言一、有哪些工作模式&#xff1f;1.1 GPIO的详细介绍1.2 GPIO的内部框图输入模式输出部分 总结 前言 在嵌入式系统开发中&#xff0c;对于STM32微控制器的GPIO&#xff08;General Purpose Input/Output&#xff09;引脚的配置和使用是至关重要的。GPIO引脚可以通…

【解决】配置文件YAML: application.yml Cannot resolve configuration property ‘xxxx‘

配置文件YAML: application.yml Cannot resolve configuration property xxxx 问题排查解决 问题 在application.yml文件里面配置Bean的初始值&#xff0c;但是报错如下&#xff1a; Cannot resolve configuration property person.lastName 排查 我们先去Bean检查是否拼写…

基于函数计算AIGC图片识别

目录 在 OSS 中建立图片目录 在函数计算中基于模板创建ImageAI应用 体验ImageAI图像识别效果 我们不但可以基于函数计算创建AIGC应用&#xff0c;实现以文生图&#xff0c;同时我们也可以基于函数计算创建ImageAI应用&#xff0c;通过简单几步实现对图片中对象的识别。下面我…

【运维测试】测试理论+工具总结笔记第1篇:测试理论的主要内容(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论测试理论测试工具相关知识。Python测试理论的主要内容&#xff0c;掌握软件测试的基本流程&#xff0c;知道软件测试的V和W模型的优缺点&#xff0c;掌握测试用例设计的要素&#xff0c;掌握等价类划分法、边界值法、因…

qt“五彩斑斓“ opengl

本篇文章我们来描述一下opengl相关知识 我们先看一下opengl渲染的效果 很漂亮&#xff1f; 那下面就来介绍一下这么漂亮的opengl OpenGL&#xff08;Open Graphics Library&#xff09;是一个跨平台的图形编程接口&#xff0c;用于渲染2D和3D图形。它提供了一系列函数和数据结…

Zotero插件分享(第二弹)

今天紧接上一篇文章&#xff08;Zotero常用插件分享&#xff09;&#xff0c;继续分享关于Zotero常用插件的相关内容。&#xff08;排名不分先后&#xff09; 1.Translate for Zotero 英文文献阅读辅助工具&#xff0c;可以实现将pdf中选中的文字翻译为指定语言&#xff0c;并…

Git中Idea操作git及Git Flow

目录 一、Idea中使用Git 1.idea配置Git和Gitee 2.实践操作 1.将本地项目推送到远程 2.从远程库克隆项目到本地 二、Git Flow 1.什么是Git Flow 2.工作流程 3.实践操作 一、Idea中使用Git 1.idea配置Git和Gitee 第一步&#xff1a;设置git.exe的安装路径 在设置中的…

RMSNorm原理及代码

RMSNorm原理及代码 在大模型中使用层归一化有如下几个因素&#xff1a; 改善网络稳定性加速收敛速度提高模型的泛化能力 批量归一化是对一个批次内的数据进行归一化 层归一化是对一个样本中的不同特征进行归一化 如下是LayerNorm与RMSNorm的公式 在LLaMA中使用RMSNorm替代…