总线设备驱动模型

Posted coding__madman

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了总线设备驱动模型相关的知识,希望对你有一定的参考价值。

总线驱动设备模型:

1. 总线设备驱动模型概述

        随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求,为适应这宗形势的需求,从linux2.6内核开始提供了全新的设备模型

2. 总线

    2.1 描述结构



    2.2 注册


    2.3 注销

void  bus_unregister(struct  bus_type *bus)

代码例程:

bus.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/device.h>

MODULE_LICENSE("GPL");

int my_match(struct device *dev, struct device_driver *drv)
{
	
	return 0;
}

struct bus_type my_bus_type = 
{
	.name = "my_bus",
	.match = my_match,
};

int my_bus_init(void)
{
	int ret;
	
	ret = bus_register(&my_bus_type);
	
	return ret;
}

void my_bus_exit(void)
{
	bus_unregister(&my_bus_type);
}


module_init(my_bus_init);
module_exit(my_bus_exit);

这是上面的总线驱动模块编译运行的效果图!


下面向上面的my_bus总线上挂载一个驱动!

3. 驱动

    3.1 描述结构


    3.2 注册

      int  drvier_register(struct  device  *dev)

    3.3 注销

      void drever_unregister(struct  device_driver  *drv)

4. 设备

    4.1 设备的描述


    4.2 设备的注册

       int device_register(struct device *dev)

    4.3 设备的注销

       void device_unregister(struct device *dev)

driver.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<linux/kernel.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

int my_probe(struct device *dev)
{
	printk(KERN_WARNING"driver found the device!!!\\n");
	
	return 0;
}

struct device_driver my_driver = 
{
	.name = "my_dev",
	.bus = &my_bus_type,
	.probe = my_probe, //当找到这个设备时将调用这个函数
};

int my_device_init(void)
{
	int ret; 
	
	ret = driver_register(&my_driver);//注册一个驱动
	
	return 0;
}

void my_device_exit(void)
{
	driver_unregister(&my_driver);
}

module_init(my_device_init);
module_exit(my_device_exit);
Makefile

obj-m := bus.o device.o
KDIR := /home/kernel/linux-ok6410
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order


下面再在总线上挂载一个设备!

device.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

struct device my_dev = 
{
     .init_name = "my_dev",//和驱动名字一样
     .bus = &my_bus_type,	
};

int my_device_init(void)
{
	int ret;
     ret = device_register(&my_dev);
     return ret;
     
}


void my_device_exit(void)
{
	device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit);

driver.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<linux/kernel.h>

MODULE_LICENSE("GPL");

extern struct bus_type my_bus_type;

int my_probe(struct device *dev)
{
	printk(KERN_WARNING"driver found the device!!!\\n");
	
	return 0;
}

struct device_driver my_driver = 
{
	.name = "my_dev",
	.bus = &my_bus_type,
	.probe = my_probe, //当找到这个设备时将调用这个函数
};

int my_device_init(void)
{
	int ret; 
	
	ret = driver_register(&my_driver);//注册一个驱动
	
	return 0;
}

void my_device_exit(void)
{
	driver_unregister(&my_driver);
}

module_init(my_device_init);
module_exit(my_device_exit);
bus.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/device.h>

MODULE_LICENSE("GPL");

int my_match(struct device *dev, struct device_driver *drv)
{
	return !strncmp(dev->kobj.name,drv->name,strlen(drv->name));
}

struct bus_type my_bus_type = 
{
	.name = "my_bus",
	.match = my_match,
};

EXPORT_SYMBOL(my_bus_type);//输出符号 另一device.c要用到

int my_bus_init(void)
{
	int ret;
	
	ret = bus_register(&my_bus_type);
	
	return ret;
}

void my_bus_exit(void)
{
	bus_unregister(&my_bus_type);
}


module_init(my_bus_init);
module_exit(my_bus_exit);

Makefile

obj-m := bus.o driver.o device.o
KDIR := /home/kernel/linux-ok6410
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order


运行效果图:



平台总线驱动设计:

1. 平台总线概述

    平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。


2. 平台设备



注册平台设备,使用函数:

    int platform_device_register(struct platform_device *pdev)


3. 平台驱动


平台驱动注册使用函数:

int platform_driver_register(struct platform_driver *)


结合上面的基础知识,将案件驱动修改为平台驱动模式!

1. 平台设备注册

2. 平台按键驱动设计

key_dev.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/interrupt.h>

MODULE_LICENSE("GPL");

#define GPNCON 0x7F008830

static struct resource key_resource[] = { //定义按键资源
	[0] = {
			  .start = GPNCON,
			  .end = GPNCON + 8,
			  .flags = IORESOURCE_MEM,//内存地址资源
		  },
    [1] = {
			  .start = S3C_EINT(0),//按键中断资源
			  .end = S3C_EINT(5),
			  .flags = IORESOURCE_IRQ,//内存地址资源
          },
};

struct platform_device key_device = 
{
	.name = "my_key",
	.id = 0,
	.num_resources = ARRAY_SIZE(key_resource),
	.resource = key_resource,
};

int keydri_init(void)
{
	platform_device_register(&key_device);
	return 0;
}

void keydri_exit(void)
{
	platform_device_unregister(&key_device);
}

module_init(keydri_init);
module_exit(keydri_exit);
编译运行截图:



key_dri.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h> /* for struct miscdevice*/
#include <linux/interrupt.h>
#include <linux/fs.h> /* for iormap */
#include <linux/io.h>
#include <linux/slab.h> /* for kmalloc */
#include<linux/uaccess.h> /* for copy_to_usr */
#include <linux/platform_device.h>

//#define GPNCON  0x7F008830
//#define GPNDAT  0x7F008834
MODULE_LICENSE("GPL");

unsigned int *key_base;

struct work_struct *work1;//定义一项工作

struct timer_list key_timer; //定义一个定时器key_timer

unsigned int key_num;

struct resource *res_mem;
struct resource *res_irq;
int size;

void work1_func(struct work_struct *work)
{
	//启动定时器 jiffies是全局变量,用来表示当前系统时间 1S=1000个滴答数
	mod_timer(&key_timer,jiffies + HZ/10); //设置100ms超时 1HZ=1S
}

void key_timer_func(unsigned long data)
{
	unsigned int key_val;
	
	key_val = readw(key_base + 1)&0x01; //只读取最后一位
	if(key_val == 0)
	{
		printk(KERN_WARNING"OK6410 key1 down!\\n");
		key_num = 0;
	}
	
	key_val = readw(key_base + 1)&0x20; //只读取最后一位
	if(key_val == 0)
	{
		printk(KERN_WARNING"OK6410 key6 down!\\n");
		key_num = 6;
	}

}

irqreturn_t key_int(int irq, void *dev_id)
{
	//1. 检测是否发生了按键中断 这里可以暂时不做,因为这里没有使用共享中断
	
	//2. 清除已经发生的按键中断 这个是指硬件内部处理,按键CPU内部不需要做处理
		 
	//3. 提交下半部
	schedule_work(work1);
	
	//return 0;
	return IRQ_HANDLED;
}

void key_hw_init(void) //按键硬件初始化部分
{
	//unsigned int *gpio_config;
	unsigned short data;
	
	//gpio_config = ioremap(GPNCON, 4);//将物理地址转化为虚拟地址
	data = readw(key_base);
	data &= ~0b110000000011; //先清零
	data |= 0b100000000010;  //后两位设置成0b10
	writew(data, key_base);
	
	//gpio_data = ioremap(GPNDAT, 4);//将物理地址转化为虚拟地址
	
	printk(KERN_WARNING"init ...!\\n");
}

int key_open(struct inode *node, struct file *filp)
{
	printk(KERN_WARNING"open ...!\\n");
	
	return 0;
}

ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
	//将key_value值返回给用户空间
	printk(KERN_WARNING"in kernel :key num is %d\\n",key_num);
	copy_to_user(buf, &key_num, 4); //buf为用户空间传过来的地址
	
	return 4;
}

struct file_operations key_fops = 
{
	.open = key_open,
	.read = key_read,
};

struct miscdevice key_miscdev = //定义一个misdevice结构
{
	.minor = 200,
	.name = "6410key",
	.fops = &key_fops,//这里key_fops是一个struct file_operations结构
};

static int __devinit key_probe(struct platform_device *pdev)
{

	misc_register(&key_miscdev);//注册一个混杂设备驱动设备
	
	res_irq =  platform_get_resource(pdev, IORESOURCE_IRQ, 0); //取出中断资源
	request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"my_key",(void *)1);
    request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"my_key",(void *)6);
	
	res_mem =  platform_get_resource(pdev, IORESOURCE_MEM, 0);//取出地址资源
	
	size = res_mem->end - res_mem->start + 1;
	key_base = ioremap(res_mem->start,size);
	
	key_hw_init();
	
	work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
	INIT_WORK(work1 , work1_func );
	
	//初始化定时器
	init_timer(&key_timer);
	key_timer.function = key_timer_func; //将定义的函数赋值给函数指针
	
	//注册定时器
	add_timer(&key_timer);
	
		
	return 0;
	
}

static int key_remove(struct platform_device *device)
{
	free_irq(S3C_EINT(0), 0);//注销中断 这里irqnumber参数暂时用一个变量来表示(中断号)
	free_irq(S3C_EINT(5), 0);//注销中断 这里irqnumber参数暂时用一个变量来表示(中断号)
	
	misc_deregister(&key_miscdev);//注销一个混杂设备驱动
	
	return 0;
}

struct platform_driver key_driver = 
{
	.driver		= {
		.name	= "my_key",
		.owner	= THIS_MODULE,
	},
	.probe		= key_probe,
	.remove		= key_remove,
};

static int key_init(void)
{
	
	return platform_driver_register(&key_driver);
	
}

static void key_exit(void)
{

	platform_driver_register(&key_driver);//卸载平台驱动
	
	printk(KERN_WARNING"key up!");
}

module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("key driver");
Makefile

obj-m := key_dev.o key_dri.o
KDIR := /home/kernel/linux-ok6410
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
编译运行效果截图:



按下按键1或者按下按键6 可以看到驱动程序打印出如下信息! 这里或者创建设备文件,然后用前面博客里面的应用程序来测试也是一样的!

以上是关于总线设备驱动模型的主要内容,如果未能解决你的问题,请参考以下文章

面向对象地分析Linux内核设备驱动——Linux内核设备模型与总线

面向对象地分析Linux内核设备驱动——Linux内核设备模型与总线

8.总线设备驱动模型

第四季-专题13-总线设备驱动模型

让天堂的归天堂,让尘土的归尘土——谈Linux的总线设备驱动模型

总线设备驱动模型