总线只是负责找到设备和驱动匹配,并调用驱动里的probe函数,在匹配后的probe函数里该注册混杂设备驱动还得注册。
平台总线的作用就是:一个驱动可以动态匹配一个系列的如三星的所有ARM芯片的看门狗驱动,当某一个三星的ARM芯片
设备插入时,总线就会动态感应到有设备插入就会去调用与这个设备匹配的驱动来驱动这个设备:获取这个设备的资源,
这就是一个驱动可以驱动多个设备的原理,而不用每一个SOC芯片都去编写一个驱动,如都是三星ARM芯片的看门狗驱动
不用每一款ARM芯片都去编写一个驱动。
所以这又是比混杂设备更加高级的驱动框架。
所有的平台总线的驱动函数都是在platform-devices.h头文件下面,这里要说明一下
在include/linux/***.h
目录下的头文件都是全平台通用的头文件,它们是与平台无关的。
就如应用层的usr/include/***.h下的头文件都是全平台通用的头文件。
代码如下:
//-总线设备驱动模型---第2课-平台设备驱动设计
/*********************************key_dev.c **********************************/
/*********************************/转载请注明源文地址:http://blog.csdn.net/oyhb_1992/article/details/77141214
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
#define GPFCON 0x56000050
//设备资源
static struct resource key_resource[] = {
[0] = {
.start = GPFCON,
.end = GPFCON + 8,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_EINT0,
.end = IRQ_EINT2,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device key_device = {
.name = "my-key",
.id = 0,
.num_resources = ARRAY_SIZE(key_resource),
.resource = key_resource,
};
static int button_init()
{
platform_device_register(&key_device);
return 0;
}
static void button_exit()
{
platform_device_unregister(&key_device);
}
module_init(button_init);
module_exit(button_exit);
/*********************************key_driver.c **********************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
MODULE_LICENSE("GPL");
//只是一个指针,还需要分配内存
struct work_struct *work;
struct timer_list buttons_timer;
unsigned int key_num = 0;
wait_queue_head_t key_q;
struct resource *res;
struct resource *res_irq;
unsigned int *key_base;
void work_func(struct work_struct *work)
{
mod_timer(&buttons_timer, jiffies + (HZ /10));//启动定时器延时1/10秒
}
void buttons_timer_function(unsigned long data)
{
unsigned int key_val;
// 读取按键值
key_val = readw(key_base+1)&0x1;
if (key_val == 0)
key_num = 4;
key_val = readw(key_base+1)&0x4;
if (key_val == 0)
key_num = 3;
wake_up(&key_q); //唤醒队列来读取键值
}
irqreturn_t key_int(int irq, void *dev_id)
{
//1. 检测是否发生了按键中断
//2. 清除已经发生的按键中断
//3. 提交下半部
schedule_work(work);
//return 0;
return IRQ_HANDLED;
}
void key_hw_init()
{
unsigned short data;
data = readw(key_base);
data &= ~0b110011;//将引脚设置为输入模式
data |= 0b100010;
writew(data,key_base);
}
int key_open(struct inode *node,struct file *filp)
{
return 0;
}
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
wait_event(key_q,key_num);
//等待按键有值可读
copy_to_user(buf, &key_num, 4);
key_num = 0;
return 4;
}
struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};
struct miscdevice key_miscdev = {
.minor = 200,
.name = "key",
.fops = &key_fops,
};
int key_probe(struct platform_device *pdev) //当匹配时,驱动就可以获得设备的platform_device结构
{
int ret,size;
ret = misc_register(&key_miscdev); //注册混杂设备
if (ret !=0)
printk("register fail!\n");
//注册中断处理程序
//取出设备里的资源,取出IORESOURCE_IRQ类型的资源的第0个资源
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
/*(void *)4是request_irq函数传递给handler中断函数的void *参数,详见http://blog.csdn.net/oyhb_1992/article/details/77341782 */
request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
//按键初始化,按键物理地址映射为虚拟地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
key_base = ioremap(res->start, size); //动态映射
key_hw_init();
// 创建工作 ,为指针分配内存
work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work, work_func); //工作队列就是执行一个函数
/* 初始化定时器 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
/* 向内核注册一个定时器 */
add_timer(&buttons_timer);
/*初始化等待队列*/
init_waitqueue_head(&key_q);
return 0;
}
//内核代码里搜索platform_driver模仿内核里的写法
int key_remove(struct platform_device *dev)
{ //设备移除时注销设备
free_irq(res_irq->start, (void *)4);
free_irq(res_irq->end, (void *)3);
iounmap(key_base);
misc_deregister(&key_miscdev);
return 0;
}
//内核代码里搜索platform_driver模仿内核里的写法
static struct platform_driver key_driver = {
.probe = key_probe, //当设备和驱动匹配时调用这个函数
.remove = key_remove,
.driver = {
.owner = THIS_MODULE,
.name = "my-key",//设备的名字必须和驱动的名字一样用于总线key_drv.c的math匹配函数
},
};
static int button_init()
{
return platform_driver_register(&key_driver); //平台驱动结构体注册
}
static void button_exit()
{
platform_driver_unregister(&key_driver);
}
module_init(button_init);
module_exit(button_exit);
/*********************************Makefile **********************************/
obj-m := key_dev.o key_driver.o
KDIR := /home/S5-driver/lesson7/linux-tq2440/
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
一些函数小语法:
&&条件的注意
//dev是在内核进行驱动和设备匹配时得到的设备的结构体, type是我们要取哪一种类型的资源,num是要取这种类型资源的第几个,
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;//注意只有在资源类型匹配时,后面的语句才会执行,例如取这种资源类型type的第一个资源,则num--==0立刻满足条件,这///里是后减减,如果取第一个资源,则1!=0,下次再次遍历到这个资源类型是0--==0满足条件,就可以取出这个资源了
}
return NULL;
}
内核注册了哪些平台总线的驱动呢?
在linux内核源码sourceinsight工程下搜索mach-smdk2440.c文件(linux/arch/arm/mach-s3c2440/mach-smdk2440.c)
找到如下函数,内核里定义了一个平台设备的结构体数组然后注册它
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
};
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
/* linux/arch/arm/plat-s3c24xx/devs.c*/
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt",//匹配时用这个名字
.id = -1,
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
.resource = s3c_wdt_resource,
};
总结:平台设备驱动框架的核心是:一个驱动可以驱动多个设备,热拔插动态匹配