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,一经查实,立即删除!

相关文章

C语言系列3——流程控制:条件语句与循环结构

目录 写在开头1. if语句的应用与语法解析2. switch语句的使用技巧3. for与while循环的比较与实际运用3.1 for循环的使用3.2 while循环的使用3.3 比较与实际运用 4. 练习题目4.1 题目介绍4.2 参考答案 写在最后 写在开头 在程序设计中&#xff0c;流程控制结构是至关重要的&…

猫头虎分享已解决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;云天数据恢复中心接到很多企业的求…

创建一个多进程服务器和多线程服务器

多进程服务器 #include<myhead.h> #define PORT 8888 //端口号 #define IP "192.168.10.10" //IP地址//定义信号处理函数&#xff0c;用于回收僵尸进程 void handler(int signo) {if(signo SIGCHLD){while(waitpid(-1, NULL, WNOHAN…

C++:IO流

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

C语言——oj刷题——获取月份天数

题目&#xff1a; 描述 KiKi想获得某年某月有多少天&#xff0c;请帮他编程实现。输入年份和月份&#xff0c;计算这一年这个月有多少天。 输入描述&#xff1a; 多组输入&#xff0c;一行有两个整数&#xff0c;分别表示年份和月份&#xff0c;用空格分隔。 输出描述&…

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<…

作业2024/2/14

指针练习 1、选择题 1.1、若有下面的变量定义&#xff0c;以下语句中合法的是&#xff08; A &#xff09;。 int i&#xff0c;a[10]&#xff0c;*p&#xff1b; A&#xff09; pa2; B&#xff09; pa[5]; C&#xff09; pa[2]2; D&#xff09; p&(i…

Go语言开发小技巧易错点100例(十二)

往期回顾&#xff1a; Go语言开发小技巧&易错点100例&#xff08;一&#xff09;Go语言开发小技巧&易错点100例&#xff08;二&#xff09;Go语言开发小技巧&易错点100例&#xff08;三&#xff09;Go语言开发小技巧&易错点100例&#xff08;四&#xff09;Go…

【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的&…

c入门第十五篇——学而时习之(阶段性总结)

古人说&#xff1a;“学而时习之。”古人又说&#xff1a;“温故而知新。”古人还说&#xff1a;“读书百遍&#xff0c;其义自见。” 总结一个道理那就是好书要反反复复的读&#xff0c;学习过的知识要时常去复习它&#xff0c;才有可能常读常新。 我&#xff1a;“师弟&…

蔚来面试解答

你的问题包含了多个方面&#xff0c;我会尽力逐一回答&#xff1a; 锁机制及锁膨胀过程&#xff1a; 锁机制是并发编程中用于控制多线程对共享资源访问的一种机制&#xff0c;以避免资源冲突导致的数据不一致问题。锁膨胀是指锁在运行时根据竞争情况可以升级的过程&#xff0c;…

【安装pytorch】

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 例如&#xff1a;项目需求安装pytorch&#xff0c;安装神经网络环境配置。 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 安装pytorch 官网 https://pytorch.org/ 详细看官方提供…

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…

C#中使用 => 运算符的 switch 表达式

目录 一、关于switch 语句 1.switch 语句及语法结构 2.switch 语句的一些重要注意事项 3.通常情况下的示例 1.示例1 2.示例2 二、使用 > 运算符的 switch 表达式 1.关于switch表达式 2.示例1 3.示例2 一、关于switch 语句 1.switch 语句及语法结构 C# 中的 swit…

有用的 Bash 别名和函数简介

简介 随着你在命令行上的操作越来越多&#xff0c;你会发现你使用的大部分命令只是可用命令的一个非常小的子集。大多数任务是习惯性的&#xff0c;你可能每天都以相同的方式运行这些任务。 虽然许多常见命令实用程序的制造者已经尝试通过使用缩短的名称&#xff08;想想每天…

【Springboot】参数校验 spring-boot-starter-validation

# 第一步&#xff1a;引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>xxx</version> </dependency> # 第二步&#xff1a;实体类添加…

Excel

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