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 用法展示:

cpp 复制代码
/*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");
cpp 复制代码
/*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

cpp 复制代码
/*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

cpp 复制代码
/*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

cpp 复制代码
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

cpp 复制代码
/*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

cpp 复制代码
/*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

cpp 复制代码
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对多

cpp 复制代码
/*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成员

cpp 复制代码
#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

cpp 复制代码
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)
相关推荐
望获linux24 分钟前
【实时Linux实战系列】Linux 内核的实时组调度(Real-Time Group Scheduling)
java·linux·服务器·前端·数据库·人工智能·深度学习
MC丶科34 分钟前
【SpringBoot常见报错与解决方案】端口被占用?Spring Boot 修改端口号的 3 种方法,第 3 种 90% 的人不知道!
java·linux·spring boot
江公望1 小时前
ubuntu kylin(优麒麟)和标准ubuntu的区别浅谈
linux·服务器·ubuntu·kylin
Lynnxiaowen1 小时前
今天我们开始学习python语句和模块
linux·运维·开发语言·python·学习
生态笔记1 小时前
PPT宏代码
linux·服务器·powerpoint
mucheni1 小时前
迅为RK3588开发板Ubuntu 系统开发ubuntu终端密码登录
linux·运维·ubuntu
skywoodsky1 小时前
Ubuntu 24.04环境下的挂起转休眠
linux
小云数据库服务专线2 小时前
GaussDB 应用侧报Read timed out解决方法
linux·服务器·gaussdb
资源补给站2 小时前
服务器高效操作指南:Python 环境退出与 Linux 终端快捷键全解析
linux·服务器·python
一苓二肆2 小时前
代码加密技术
linux·windows·python·spring·eclipse