总线设备驱动模型

一、总线设备驱动模型

总线设备驱动模型采用分层分离的方式来进行,采用bus/dev/drv的模型,把设备从驱动中剥离出来。
在这里插入图片描述

二、platform总线模型详解

首先查看bus_type实例platform_bus_type.

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,	//bind platform device to platform driver.
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};

其中platform_match函数(在注册驱动时,这个函数会在driver_match_device(drv, dev)函数中调用),比较的顺序如下。

  1. platform_device. driver_override和 platform_driver.driver.name
  2. 设备树
  3. platform_device. name和 platform_driver.id_table[i].name
  4. platform_device.name和 platform_driver.driver.name
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

再次,查看注册平台设备和注册平台驱动时的流程
注册平台设备:

int platform_device_register(struct platform_device *pdev)
	platform_device_add(pdev);
		device_add(&pdev->dev);				//struct device
			error = bus_add_device(dev);	//放入链表
			bus_probe_device(dev);			//probe drivers for a new device
				device_initial_probe(dev);
					__device_attach(dev, true);
						bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);	//在drv链表中, bus_type->subsys_private->klist_drivers
							__device_attach_driver				//attempt to bind device & driver together.			struct device_driver和struct device
								driver_match_device(drv, dev);	//查看drv和dev是否匹配,drv->bus->match ? drv->bus->match(dev, drv) : 1;在这里会调用bus里面的match函数,指向了platform_match
								driver_probe_device(drv, dev);	//attempt to bind device & driver together,调用drv的probe函数
									really_probe(dev, drv);
										dev->driver = drv;		//dev里面的driver = drv。将两者联系起来了。
										dev->bus->probe(dev);	//在platform_bus_type中没有定义probe函数,所以会调用drv->probe(dev)函数
										//或者
										drv->probe(dev);

注册平台驱动:

platform_driver_register(drv) 
	int __platform_driver_register(struct platform_driver *drv, struct module *owner)
		drv->driver.owner = owner;
		drv->driver.bus = &platform_bus_type;
		drv->driver.probe = platform_drv_probe;
		drv->driver.remove = platform_drv_remove;
		drv->driver.shutdown = platform_drv_shutdown;
		driver_register(&drv->driver);	//register driver with bus
			bus_add_driver(drv);		//放入链表,这种bus type的驱动列表
				driver_attach(drv);		//try to bind driver to devices.
					bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
						__driver_attach
							driver_match_device(drv, dev);	//查看drv和dev是否匹配,drv->bus->match ? drv->bus->match(dev, drv) : 1;在这里会调用bus里面的match函数,指向了platform_match
							device_driver_attach(drv, dev);	//attach a specific driver to a specific device
								driver_probe_device(drv, dev);
									really_probe(dev, drv);
										dev->driver = drv;		//dev里面的driver = drv。将两者联系起来了。
										dev->bus->probe(dev);	//在platform_bus_type中没有定义probe函数,所以会调用drv->probe(dev)函数
										//或者
										drv->probe(dev);

三、LED软件架构

在这里插入图片描述

四、详细代码

4.1 led_drv.c


#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_operations.h"

#define LED_NUM 2
/*1、确定主设备号*/
static int major = 0;
//static char kernel_buf[1024];
static struct class *led_class;
struct led_operations* p_led_opr;

#define MIN(a,b) (a < b ? a : b)


static ssize_t led_drv_read (struct file *, char __user *, size_t, loff_t *);
static ssize_t led_drv_write (struct file *, const char __user *, size_t, loff_t *);
static int led_drv_open (struct inode *, struct file *);
static int led_drv_close (struct inode *, struct file *);



void led_class_create_device(int minor)
{
	device_create(led_class, NULL, MKDEV(major, minor), NULL, "tanzhenwen_led%d",minor);// /dev/tanzhenwen_led0,设备节点
}
EXPORT_SYMBOL(led_class_create_device);

void led_class_destroy_device(int minor)
{
	device_destroy(led_class, MKDEV(major, minor));
}
EXPORT_SYMBOL(led_class_destroy_device);

void register_led_operations(struct led_operations *opr)
{
	p_led_opr = opr;
}
EXPORT_SYMBOL(register_led_operations);



/*2、定义自己的file_operations结构体*/
static const struct file_operations led_drv_fops = {
	.owner		= THIS_MODULE,
	.open		= led_drv_open,
	.read		= led_drv_read,
	.write		= led_drv_write,
	.release	= led_drv_close,
};
	

/*3、实现对应的open、read、write等函数,填入file_operations结构体内*/

static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
	//int err;
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	//err = copy_to_user(buf, kernel_buf, MIN(1024, size));
	return 0;
}

static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{
	char status;
	struct inode *inode = file_inode(file);
	int dev = iminor(inode) & 0x0f;
	
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	copy_from_user(&status, buf, 1);
	/*根据此设备号和status控制LED*/
	p_led_opr->ctl(dev,status);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	int minor = iminor(node) & 0x0f;
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	/*根据此设备号初始化结构体*/
	//p_led_opr->init(minor);
	p_led_opr->init();
	p_led_opr->open(minor);
	return 0;

}
static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	return 0;
}



/*4、吧file_operations结构体告诉内核:注册驱动程序*/

/*5、谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数*/
static int __init led_init(void)
{
	int err;
//	int i;
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "tanzhenwen_led", &led_drv_fops);

	//自动创建设备节点
	led_class = class_create(THIS_MODULE, "tanzhenwen_led_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class))
	{
		unregister_chrdev(major,"tanzhenwen_led");
		return -1;
	}
	//在这里如果这么用的话,小心在注册这个驱动的时候,p_led_opr为空
	//p_led_opr->init();
	/*
	for(i=0; i<LED_NUM; i++)
	{
		device_create(led_class, NULL, MKDEV(major, i), NULL, "tanzhenwen_led%d",i);// /dev/tanzhenwen_led0,设备节点
	}
	*/
	//p_led_opr = get_board_led_opr();
	return 0;
}



/*6、有入口函数就有出口函数:卸载驱动时就会去调用这个出口函数*/
static void __exit led_exit(void)
{
//	int i;
	printk("%s %s line %d\n",__FILE__, __FUNCTION__, __LINE__);
	/*
	for(i = 0; i < LED_NUM; i++)
	{
		device_destroy(led_class, MKDEV(major, i));
	}
	*/
	//在这里如果这么用的话,小心在卸载这个驱动的时候,p_led_opr为空
	if(p_led_opr)
		p_led_opr->exit();
	class_destroy(led_class);
	unregister_chrdev( major, "tanzhenwen_led");
}



/*7、其它完善:提供设备信息,自动创建设备节点*/

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tanzhenwen");


led_drv.h


#ifndef _LED_DRV_H
#define _LED_DRV_H

#include "led_operations.h"

void led_class_create_device(int minor);
void led_class_destroy_device(int minor);
void register_led_operations(struct led_operations *opr);

#endif /* _LEDDRV_H */

4.2 chip_demo_gpio.c

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/platform_device.h>
#include <linux/module.h>
#include <asm/io.h>



#include "led_operations.h"
#include "led_resource.h"
#include "led_drv.h"


/* 寄存器 */
//RCC PLL4 Control Register (RCC_PLL4CR) 0x50000000+0x894	//使能PLL4时钟
static volatile unsigned int *RCC_PLL4CR;	

//RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28					//使能GPIO
static volatile unsigned int *RCC_MP_AHB4ENSETR;

//GPIOA_MODER地址:0x50002000 + 0x00,设置bit[21:20]=0b01,		//PA10输出
static volatile unsigned int *GPIOA_MODER;

//GPIOA_ODR地址: 0x50002000 + 0x14							//读寄存、修改值、写回去(低效)
static volatile unsigned int *GPIOA_ODR;

//GPIOA_BSRR地址: 0x50002000 + 0x18							// 直接写寄存器,一次操作即可,高效
static volatile unsigned int *GPIOA_BSRR;

//GPIOG_MODER地址:0x50008000 + 0x00,							//PG8输出
static volatile unsigned int *GPIOG_MODER;
//GPIOG_ODR地址: 0x50008000 + 0x14							//读寄存、修改值、写回去(低效)
static volatile unsigned int *GPIOG_ODR;


//GPIOG_BSRR地址: 0x50008000 + 0x18							// 直接写寄存器,一次操作即可,高效
static volatile unsigned int *GPIOG_BSRR;



static int g_ledpins[100];
static int g_ledcnt = 0;

static int board_demo_led_init(void)
{
	/*
	printk("init gpio:group %d, pin %d \n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
	switch(GROUP(g_ledpins[which]))
	{
		case 0 :	printk("init the pin of group 0 ... ");break;
		case 1 :	printk("init the pin of group 1 ... ");break;
		case 2 :	printk("init the pin of group 2 ... ");break;
		case 3 :	printk("init the pin of group 3 ... ");break;
	}
	*/
	/* ioremap :使用虚拟地址,后期可以用虚拟地址来访问寄存器 */
	//ioremap(mem_addr, mem_len);
	//RCC PLL4 Control Register (RCC_PLL4CR) 0x50000000+0x894	//使能PLL4时钟
	if(!RCC_PLL4CR)
	{
		RCC_PLL4CR = ioremap(0x50000000+0x894, 4);	
	
		//RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28					//使能GPIO
		RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);
		
		//GPIOA_MODER地址:0x50002000 + 0x00,设置bit[21:20]=0b01,		//PA10输出
		GPIOA_MODER = ioremap(0x50002000 + 0x00, 4);
		
		//GPIOA_ODR地址: 0x50002000 + 0x14							//读寄存、修改值、写回去(低效)
		GPIOA_ODR = ioremap(0x50002000 + 0x14, 4);
		
		//GPIOA_BSRR地址: 0x50002000 + 0x18 						// 直接写寄存器,一次操作即可,高效
		GPIOA_BSRR = ioremap(0x50002000 + 0x18, 4);


		//PG8输出
		//GPIOG_MODER地址:0x50008000 + 0x00,							
		GPIOG_MODER = ioremap(0x50008000 + 0x00, 4);
		//GPIOG_ODR地址: 0x50008000 + 0x14							//读寄存、修改值、写回去(低效)
		GPIOG_ODR = ioremap(0x50008000 + 0x14, 4);
		//GPIOG_BSRR地址: 0x50008000 + 0x18							// 直接写寄存器,一次操作即可,高效
		GPIOG_BSRR = ioremap(0x50008000 + 0x18, 4);
	}

	printk("%s %s %d, led\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static void board_stm32mp157_led_exit(void)
{
	iounmap(RCC_PLL4CR);
	iounmap(RCC_MP_AHB4ENSETR);
	
	iounmap(GPIOA_MODER);
	iounmap(GPIOA_ODR);
	iounmap(GPIOA_BSRR);

	iounmap(GPIOG_MODER);
	iounmap(GPIOG_ODR);
	iounmap(GPIOG_BSRR);
}


static int board_stm32mp157_led_open(int which)
{
	
	if(which == 0)
	{
		/* 配置gpio,使能、配置为gpio,配置输入输出 */
		//配置时钟
		*RCC_PLL4CR |= (1<<0);
		while((*RCC_PLL4CR & (1<<1)) == 0);

		//使能GPIOA
		*RCC_MP_AHB4ENSETR |= (1<<0);

		//设置PA10用作输出,先清零,在设置为输出
		*GPIOA_MODER &= ~(3<<20);
		*GPIOA_MODER |= (0x01<<20);
	}
	else if(which == 1)
	{
		/* 配置gpio,使能、配置为gpio,配置输入输出 */
		//PG8
		//配置时钟
		*RCC_PLL4CR |= (1<<0);
		while((*RCC_PLL4CR & (1<<1)) == 0);

		
		//使能GPIOG
		*RCC_MP_AHB4ENSETR |= (1<<6);

		//设置PG8用作输出,先清零,在设置为输出
		*GPIOG_MODER &= ~(3<<16);
		*GPIOG_MODER |= (0x01<<16);
	}
	return 0;
}



static int board_demo_led_ctl(int which, char status)
{
	//printk("%s %s %d, led %d,%s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
	printk("set led %s:group %d, pin %d \n", \
				status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
	/*
	switch(GROUP(g_ledpins[which]))
	{
		case 0 :	printk("set the pin of group 0 ... ");break;
		case 1 :	printk("set the pin of group 1 ... ");break;
		case 2 :	printk("set the pin of group 2 ... ");break;
		case 3 :	printk("set the pin of group 3 ... ");break;
	}*/
	if(which == 0)
	{
		if(status)
		{	
			//灯亮
			*GPIOA_BSRR = (1<<26);	//输出低电平 GPIOA10
		}
		else
		{
			//灯灭
			*GPIOA_BSRR = (1<<10); //输出高电平
		}
	}
	else if(which == 1)
	{
		if(status)
		{	
			//灯亮
			*GPIOG_BSRR = (1<<24);	//输出低电平 GPIOA10
		}
		else
		{
			//灯灭
			*GPIOG_BSRR = (1<<8); //输出高电平
		}
	}
	return 0;
}

static struct led_operations board_demo_led_opr = {
	.init 	= board_demo_led_init,
	.open   = board_stm32mp157_led_open,
	.exit	= board_stm32mp157_led_exit,
	.ctl 	= board_demo_led_ctl,
};

/*
 * 
 */
static int chip_demo_gpio_probe(struct platform_device *dev)
{
	int i = 0;
	struct resource *res;
	/* 记录引脚 */
	while(1)
	{
		/* 获取资源 */
		res = platform_get_resource(dev, IORESOURCE_IRQ, i++);
		if(!res)	break;
		g_ledpins[g_ledcnt] = res->start;
		
		/* device create */
		led_class_create_device(g_ledcnt);
		g_ledcnt++;
	}
	printk("end of chip_demo_gpio_probe\n");
	
	return 0;
}
static int chip_demo_gpio_remove(struct platform_device *dev)
{
	int i;
	/* device destroy */
	for(i = 0; i< g_ledcnt; i++)
	{
		led_class_destroy_device(i);
	}
	g_ledcnt = 0;
	return 0;
}


static struct platform_driver chip_demo_gpio_drv = {
	.probe		= chip_demo_gpio_probe,
	.remove		= chip_demo_gpio_remove,
	.driver		= {
		.name	= "tanzhenwen_led",
	},
};

static int chip_demo_gpio_drv_init(void)
{
	int err;
	printk("start of chip_demo_gpio_probe\n");
	err = platform_driver_register(&chip_demo_gpio_drv);	//最终会调用chip_demo_gpio_probe函数
	register_led_operations(&board_demo_led_opr);	//注册board_demo_led_opr结构体,告诉led_drv.c中
	printk("end of chip_demo_gpio_probe\n");
	return 0;
}

static void chip_demo_gpio_drv_exit(void)
{
	platform_driver_unregister(&chip_demo_gpio_drv);
}

module_init(chip_demo_gpio_drv_init);
module_exit(chip_demo_gpio_drv_exit);
MODULE_LICENSE("GPL");

led_operations.h

#ifndef _LED_OPERATIONS_H_
#define _LED_OPERATIONS_H_

struct led_operations{
	int (*init) (void); /*初始化LED,which-哪一个LED*/
	int (*open) (int which);
	void (*exit) (void);
	int (*ctl) (int which, char status);/*控制对应的LED的亮灭*/
	//int (*read) (int which,char status);/*读对应引脚的状态*/
};

struct led_operations *get_board_led_opr(void);
#endif

led_resource.h

#ifndef _LED_RESOURCE_H_
#define _LED_RESOURCE_H_

/* bit[31:16] = gropu*/
/* bit[15:0]  = which pin*/
#define GROUP(x) 	(x>>16)
#define PIN(x)		(x&0xFFFF)
#define GROUP_PIN(g,pin)	((g<<16) | (pin))
struct led_resource{
	int pin;
};

struct led_resource *get_led_resource(void);
#endif

4.3 board_A_led.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/ioport.h>
#include <linux/platform_device.h>
#include "led_resource.h"


static struct resource board_A_resources[] = 
{
	{
		.start	= GROUP_PIN(1,10),
		//.end	= ,
		.flags  	= IORESOURCE_IRQ,
	},
	{
		.start	= GROUP_PIN(7,8),
		//.end	= ,
		.flags  	= IORESOURCE_IRQ,
	}
};

static void led_drv_release(struct device *dev)
{

}



static struct platform_device board_A_dev = {
	.name = "tanzhenwen_led",
	.resource = board_A_resources,
	.num_resources = ARRAY_SIZE(board_A_resources),
	.dev = {
		.release = led_drv_release,
	},
};

static int led_dev_init(void)
{
	int err;
	err = platform_device_register(&board_A_dev);
	return 0;
}

static void led_dev_exit(void)
{
	platform_device_unregister(&board_A_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);


MODULE_AUTHOR("Tanzhenwen");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

4.4 Makefile



KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order  
	rm -f hello_drv_test

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o

# led_drv.c board_demo.c 编译成 100ask.ko
obj-m := led_drv.o board_A_led.o chip_demo_gpio.o

四、测试

led_test.c


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./ledtest /dev/100ask_led0 on
 * ./ledtest /dev/100ask_led0 off
 */
int main(int argc, char **argv)
{
	int fd;
	char status;
	
	/* 1. 判断参数 */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	/* 3. 写文件 */
	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		write(fd, &status, 1);
	}
	else
	{
		status = 0;
		write(fd, &status, 1);
	}
	
	close(fd);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值