关闭

platform总线、设备、驱动模型之led驱动实例

标签: linux驱动platform
554人阅读 评论(0) 收藏 举报
分类:

Linux 2.6 的设备驱动模型中,关心总线、设备和驱动这 3 个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是 Linux系统提供的一种附加手段


platform.device结构体
1 struct platform_device {
2 const char * name; / * 设备名 */
3 u32 id;
4 struct device dev;
5 u32 num_resources; / * 设备所使用各类资源数量 */
6 struct resource * resource; / * 资源 */
7 };


platform_driver 这个结构体中包含 probe()remove()shutdown()suspend()resume()函数,通常也需要由驱动实现。
1 struct platform_driver {
2 int (*probe)(struct platform_device *);
3 int (*remove)(struct platform_device *);
4 void (*shutdown)(struct platform_device *);
5 int (*suspend)(struct platform_device *, pm_message_t state);
6 int (*suspend_late)(struct platform_device *, pm_message_t state);
7 int (*resume_early)(struct platform_device *);
8 int (*resume)(struct platform_device *);
9 struct pm_ext_ops *pm;
10 struct device_driver driver;
11};



系统中为platform 总线定义了一个 bus_type 的实例 platform_bus_type。
1 struct bus_type platform_bus_type = {
2 .name = "platform",
3 .dev_attrs = platform_dev_attrs,
4 .match = platform_match,
5 .uevent = platform_uevent,
6 .pm = PLATFORM_PM_OPS_PTR,
7 };
8 EXPORT_SYMBOL_GPL(platform_bus_type);


这里要重点关注其match()成员函数,正是此成员函数确定了platform_device platform_driver之间如何匹配。
1 static int platform_match(struct device *dev, struct device_driver *drv)
2 {
3 struct platform_device *pdev;
4
5 pdev = container_of(dev, struct platform_device, dev);
6 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
7 }

匹配platform_device platform_driver 主要看两者的 name字段是否相同。

匹配实例:以触摸屏驱动分析;
平台设备部分:


平台设备列表:

将设备结构s3c_device_ts 注册进内核:


平台驱动部分;

内核启动过程中, 当平台设备的名字“ s3c64xx-ts” 和平台驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()。

platform_device 的定义通常在 BSP 的板文件中实现,在板文件中,将 platform_device归纳为一个数组,最终通过platform_add_devices()函数统一注册。platform_add_devices()函数可以将平台设备添加到系统中,这个函数的原型为:
int platform_add_devices(struct platform_device **devs, int num);

该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它内部调用了platform_device_register()函数用于注册单个的平台设备

platform_device结构体定义的第 56行,描述了 platform_device的资源,资源本身由 resource 结构体描述:
1 struct resource {
2 resource__size_t start;
3 resource_size_t end;
4 const char *name;
5 unsigned long flags;
6 struct resource *parent, *sibling, *child;
7 };


设备驱动中引入platform 的概念至少有如下两大好处:
1)使得设备被挂接在一个总线上,因此,符合Linux 2.6 的设备模型。其结果是,配套的sysfs 结点、设备电源管理都成为可能。
2)隔离 BSP 和驱动。在 BSP 中定义 platform 设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API 去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。



led platform驱动实例:

led_dev.c:定义资源
#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>


/* 分配/设置/注册一个platform_device */

static struct resource led_resource[] = {
    [0] = {
        .start = 0xE0200C40,
        .end   = 0xE0200C40 + 8 - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 4,
        .end   = 4,
        .flags = IORESOURCE_IRQ,
    }

};

static void led_release(struct device * dev)
{
}


static struct platform_device led_dev = {
    .name         = "<span style="color:#ff6666;">myled</span>",
    .id       = -1,
    .num_resources    = ARRAY_SIZE(led_resource),
    .resource     = led_resource,
    .dev = { 
    	.release = led_release, 
	},
};

static int led_dev_init(void)
{
	platform_device_register(&led_dev);
	return 0;
}

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

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");



led_drv.c驱动程序:
/* 分配/设置/注册一个platform_driver */

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>

static int major;


static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;


static int leds_open(struct inode *inode, struct file *file)
{
    printk("led_open\n");

	*gpio_con = 0x11111111;
	
    return 0;
}



static long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) 
{  
	printk("use ioctl\n");
	
	
	if(cmd == 1){
		printk("arg = = %d\n",arg);
		printk("cmd = = %d\n",cmd);
		*gpio_dat =0x1f;// ~(0<<4);
		printk("gpio_dat == %0x\n",*gpio_dat);
	}
	else
   		*gpio_dat &= 0x00;
} 


static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
	.open    = leds_open,
    .unlocked_ioctl = leds_ioctl,
};

static int led_probe(struct platform_device *pdev)
{

	struct resource *res;

	res = platform_get_resource(pdev,IORESOURCE_MEM,0);
	gpio_con = ioremap(res->start,res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resourse(pdev,IORESOURCE_IRQ,0);
	pin = res -> start;

	printk("led probe\n");

	major = register_chrdev(0 , "myled", &led_fops);

	cls = class_create(THIS_MODULE,"myled");

	class_device_create(cls,NULL,MKDEV(major,0),NULL,"led");
	
	return 0;
}

static int led_remove(struct platform_device *pdev)
{
	printk("led remove\n");
	
	return 0;
}


struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "<span style="color:#ff6666;">myled</span>",
	}
};


static int led_drv_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");




内核启动过程中, 当平台设备的名字“myled"驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()








0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:11372次
    • 积分:219
    • 等级:
    • 排名:千里之外
    • 原创:11篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类