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) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_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.o

endif

编译测试

 

二、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) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_MODULE_SIG=n
obj-m += fs4412_led_device_idmatch.o
obj-m += fs4412_led_driver_idmatch.o

endif

 编译测试

三、设备树匹配

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

注意事项:

  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) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_MODULE_SIG=n
#obj-m += fs4412_led_device_idmatch.o
#obj-m += fs4412_led_driver_idmatch.o
obj-m += fs4412_led_driver_treematch.o


endif

测试 

五、一个编写驱动用的宏

熟悉这样的写法,等同于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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux环境中的git

目录 1.要使用git&#xff0c;首先要安装git 2.首次使用git需要做的操作 3.git操作 1.要使用git&#xff0c;首先要安装git 指令&#xff1a;sudo yum install -y git 2.首次使用git需要做的操作 在gitee网页&#xff0c;在你的仓库中找到&#xff1a; 先将下面两行代码分别…

基于PSO粒子群优化的PID控制器参数整定算法matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 PID控制器简介 4.2 PSO算法原理 4.3 基于PSO的PID参数整定 5.完整工程文件 1.课题概述 基于PSO粒子群优化的PID控制器参数整定。通过PSO不断的优化&#xff0c;使得PID控制器的控制反馈误差逐渐接…

《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理

《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理 总结9.1 操作符 offset问题 9.1 9.2 jmp 指令9.3 依据位移进行转移的 jmp 指令jmp short 标号程序 9.1程序 9.2图 9.2 程序 9.2 的机器码 jmp near ptr 标号 9.4 转移的目的地址在指令中的 jmp 指令如何选择 jmp short、jmp n…

干货 | 实战演练基于加密接口测试测试用例设计

如果接口测试仅仅只是掌握一些requests或者其他一些功能强大的库的用法&#xff0c;是远远不够的&#xff0c;还需要具有根据公司的业务以及需求去定制化一个接口自动化测试框架能力。所以在这个部分&#xff0c;会主要介绍接口测试用例分析以及通用的流程封装是如何完成的。 首…

用HTML Canvas和JavaScript创建美丽的花朵动画效果

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>flower</title><style>* {margin: 0;padding: 0;overflow: hidden;backg…

学习笔记20:牛客周赛32

D 统计子节点中1的个数即可&#xff08;类似树形dp&#xff1f;&#xff09; #include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<vector> #include<set> #include<map>u…

Nvidia 推出了本地版聊天 Chat with RTX;OpenAI联创Karpathy宣布离职专注个人项目

&#x1f989; AI新闻 Nvidia 推出了本地版聊天 Chat with RTX 摘要&#xff1a;英伟达最近发布了名为“Chat with RTX”的Demo版个性化AI聊天机器人&#xff0c;适用于Windows平台&#xff0c;需要Nvidia的30系/40系显卡&#xff0c;显存至少为8GB&#xff0c;系统配置包括1…

【教学类-19-05】20240214《ABAB式-规律黏贴18格-手工纸15*15CM-一页一种图案,A空,横向、边框》(中班)

背景需求 利用15*15CM手工纸制作AB色块手环&#xff08;手工纸自带色彩&#xff09; 素材准备 代码展示 作者&#xff1a;阿夏 时间&#xff1a;2024年2月14日 名称&#xff1a;正方形数字卡片AB图案 _ 华光彩云_CNKI A的位置有图案 18格 一页一种图案&#xff0c;A空&#…

步步深入 k8s 使用 pv pvc sc 在 nfs 基础上共享存储

博客原文 文章目录 前言集群环境nfs 环境搭建pod 挂载 nfs架构图 pvc 方式挂载 nfs架构图 storageclass 方式动态申请 pv架构图 参考 前言 持久化卷&#xff08;Persistent Volume, PV&#xff09;允许用户将外部存储映射到集群&#xff0c;而持久化卷申请&#xff08;Persist…

open ai api 国内配置代理指南(网上最全)

1.配置须知 open ai 作为这一波AI浪潮的推动者&#xff0c;opne ai的gpt 系列产品在使用和体验上绝对是最强大的&#xff0c;现在对于开发者来说要在代码中访问open ai api是不可用的。所以本文就主要解决这个问题。我们要了解open ai 的网站gpt的访问和api的访问收费是分开来…

K8sGPT 的使用

K8sGPT 介绍 k8sgpt 是一个扫描 Kubernetes 集群、诊断和分类问题的工具。它将 SRE 经验编入其分析器中&#xff0c;并帮助提取最相关的信息&#xff0c;通过人工智能来丰富它。它还可以与 OpenAI、Azure、Cohere、Amazon Bedrock 和本地模型结合使用。 K8sGPT Github 地址 …

波奇学Linux:文件系统

磁盘认识 磁盘被访问的基本单元是扇区-512字节。 磁盘可以看成多个同心圆&#xff0c;每个同心圆叫做磁道&#xff0c;多个扇区组成同心圆。 我们可以把磁盘看做由无数个扇区构成的存储介质。 要把数据存到磁盘&#xff0c;先定位扇区&#xff0c;用哪一个磁头&#xff0c;…

【Javascript】内存泄漏

JavaScript 内存泄露指的是在程序中&#xff0c;不再使用的内存没有被正确释放&#xff0c;导致内存占用持续增加&#xff0c;最终引发性能问题甚至崩溃。 通常哪些操作会造成内存泄漏呢&#xff1f; 未使用 var 声明的全局变量&#xff1a;在 JavaScript 中&#xff0c;如果…

Java与JavaScript的区别与联系

Java是目前编程领域使用非常广泛的编程语言&#xff0c;相较于JavaScript&#xff0c;Java更被人们熟知。很多Java程序员想学门脚本语言&#xff0c;一看JavaScript和Java这么像&#xff0c;很有亲切感&#xff0c;那干脆就学它了&#xff0c;这也间接的帮助了JavaScript的发展…

【Py/Java/C++三种语言详解】LeetCode每日一题240215【二叉树BFS】LeetCode107、二叉树的层序遍历II

有LeetCode交流群/华为OD考试扣扣交流群可加&#xff1a;948025485 可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1336了解算法冲刺训练 文章目录 题目链接题目描述解题思路DFS和BFS异同用队列维护的BFS 代码PythonJavaC时空复杂度 相关习题华为OD算法/大…

Vue2学习第一天

Vue2 学习第一天 1. 什么是 vue? Vue 是一套用于构建用户界面的渐进式框架。 2. vue 历史 vue 是在 2013 年创建的&#xff0c;vue3 是 2020 出现的&#xff0c;现在主要是用 vue2&#xff0c;创新公司用的是 vue3 vue 的作者是尤雨溪&#xff0c;vue 的搜索热度比 react…

java的面向对象编程(oop)——认识泛型

前言&#xff1a; 打好基础&#xff0c;daydayup! 泛型 1&#xff0c;认识泛型&#xff1a; 定义类&#xff0c;接口&#xff0c;方法时&#xff0c;同时声明了一个或多个类型变量&#xff08;例&#xff1a;<E>&#xff09;,称为泛型&#xff0c;泛型接口&#xff0c;泛…

计算机网络——11EMail

EMail 电子邮件&#xff08;EMail&#xff09; 3个主要组成部分 用户代理邮件服务器简单邮件传输协议&#xff1a;SMTP 用户代理 又名“邮件阅读器”撰写、编辑和阅读邮件输入和输出邮件保存在服务器上 邮件服务器 邮箱中管理和维护发送给用户的邮件输出报文队列保持待发…

###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 延时函数的生成 1.通过延时计算器得到延时函数 2.可赋值改变…

2月14日作业

1.请编程实现二维数组的杨慧三角 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf("%d",&n);int arr[n][n];for(…