您的位置:首页 > 其它

总线设备驱动模型---platform篇

2013-12-19 20:50 134 查看
2012-09-02 10:57:50

分类: LINUX

linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,设备用platform_device表示,驱
动用platform_driver进行注册。于传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用
资源,提高了代码的安全性和可移植性。
下面来看看内核时怎么注册platform总线的过程

点击(此处)折叠或打开

int __init
platform_bus_init(void)

{

int
error;

early_platform_cleanup();

error
= device_register(&platform_bus);//注册了platform的设备

if
(error)

return
error;

error
= bus_register(&platform_bus_type);//注册了platform总线

if
(error)

device_unregister(&platform_bus);

return
error;

}

点击(此处)折叠或打开

struct device platform_bus = {

.init_name =
"platform",

};

点击(此处)折叠或打开

struct bus_type platform_bus_type = {

.name =
"platform",

.dev_attrs =
platform_dev_attrs,

.match =
platform_match,

.uevent =
platform_uevent,

.pm =
&platform_dev_pm_ops,

};

其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为

1、定义一个platform_device,并注册
2、定义一个platform_driver,并注册
定义platform_device过程

点击(此处)折叠或打开

struct platform_device *platform_device_alloc(const
char *name, int
id)

{

struct
platform_object *pa;

pa
= kzalloc(sizeof(struct platform_object) +
strlen(name),
GFP_KERNEL);

if
(pa) {

strcpy(pa->name,
name);

pa->pdev.name
= pa->name;

pa->pdev.id
= id;

device_initialize(&pa->pdev.dev);

pa->pdev.dev.release
=
platform_device_release;

arch_setup_pdev_archdata(&pa->pdev);

}

return
pa ? &pa->pdev : NULL;

}

下面首先来看看platform_device的注册过程

点击(此处)折叠或打开

struct platform_device {

const
char *
name;

//设备名

int id;

//设备ID

struct
device dev;

u32 num_resources;

//设备使用的资源的数目

struct
resource *
resource;

//设备使用的资源

const
struct
platform_device_id *id_entry;

/*
MFD cell pointer */

struct
mfd_cell *mfd_cell;

/*
arch specific additions */

struct
pdev_archdata archdata;

};

点击(此处)折叠或打开

struct resource {

resource_size_t
start;

resource_size_t
end;

const
char *name;

unsigned
long flags;

struct
resource *parent, *sibling, *child;

};

在struct
resource结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型,flags可以用内存资源,IO资源,中断资源等。

点击(此处)折叠或打开

int
platform_device_register(struct
platform_device *pdev)

{

device_initialize(&pdev->dev);//dev初始化

arch_setup_pdev_archdata(pdev);

return
platform_device_add(pdev);//加入到dev链表

}

点击(此处)折叠或打开

int
platform_device_add(struct
platform_device *pdev)

{

int
i, ret = 0;

if
(!pdev)

return
-EINVAL;

if
(!pdev->dev.parent)

pdev->dev.parent
= &platform_bus;//父设备设置为platform_bus

pdev->dev.bus
= &platform_bus_type;//设备挂载在platforrm总线上

if
(pdev->id
!= -1)

dev_set_name(&pdev->dev,
"%s.%d", pdev->name, pdev->id);

else

dev_set_name(&pdev->dev,
"%s", pdev->name);

for
(i = 0; i
<</span> pdev->num_resources; i++)
{
//完成资源的初始化

struct
resource *p, *r
= &pdev->resource[i];

if
(r->name ==
NULL)

r->name
= dev_name(&pdev->dev);

p
= r->parent;

if
(!p)
{

if
(resource_type(r)
== IORESOURCE_MEM)

p
= &iomem_resource;

else
if (resource_type(r)
== IORESOURCE_IO)

p
= &ioport_resource;

}

if
(p && insert_resource(p,
r)) {

printk(KERN_ERR

"%s: failed to claim resource
%d\n",

dev_name(&pdev->dev),
i);

ret
= -EBUSY;

goto
failed;

}

}

pr_debug("Registering
platform device '%s'. Parent at %s\n",

dev_name(&pdev->dev),
dev_name(pdev->dev.parent));

ret
= device_add(&pdev->dev);

if
(ret ==
0)

return
ret;

failed:

while
(--i
>= 0)
{

struct
resource *r = &pdev->resource[i];

unsigned
long type =
resource_type(r);

if
(type ==
IORESOURCE_MEM || type ==
IORESOURCE_IO)

release_resource(r);

}

return
ret;

}

其上最后也是调用device_add的,其主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程中,再看看驱动的注册过程。

点击(此处)折叠或打开

struct platform_driver {

int
(*probe)(struct platform_device *);

int
(*remove)(struct platform_device *);

void
(*shutdown)(struct platform_device *);

int
(*suspend)(struct platform_device *,
pm_message_t state);

int
(*resume)(struct platform_device *);

struct
device_driver driver;

const
struct platform_device_id *id_table;

};

可见,它包含了设备操作的功能函数,同时包含了device_driver结构。内核提供的platform_driver结构的注册为

点击(此处)折叠或打开

int
platform_driver_register(struct
platform_driver *drv)

{

drv->driver.bus
= &platform_bus_type;//注册到总线上,drv与dev匹配通过platform_bus_type注册的platform_match完成

if
(drv->probe)

drv->driver.probe
=
platform_drv_probe;

if
(drv->remove)

drv->driver.remove
=
platform_drv_remove;

if
(drv->shutdown)

drv->driver.shutdown
=
platform_drv_shutdown;

return
driver_register(&drv->driver);

}

其注册函数中比较重要的还是调用了driver_register,添加到platform总线链表,完成设备与驱动之间的匹配过程,其主要的过程在总线设备驱动模型的驱动篇已经有过分析。

点击(此处)折叠或打开

int
driver_register(struct
device_driver *drv)

{

int
ret;

struct
device_driver *other;

BUG_ON(!drv->bus->p);

if
((drv->bus->probe && drv->probe) ||

(drv->bus->remove && drv->remove) ||

(drv->bus->shutdown && drv->shutdown))

printk(KERN_WARNING
"Driver '%s' needs updating - please
use "

"bus_type
methods\n",
drv->name);

other
= driver_find(drv->name, drv->bus);

if
(other) {

put_driver(other);

printk(KERN_ERR
"Error: Driver '%s' is already
registered, "

"aborting...\n",
drv->name);

return
-EBUSY;

}

ret
= bus_add_driver(drv);

if
(ret)

return
ret;

ret
= driver_add_groups(drv,
drv->groups);

if
(ret)

bus_remove_driver(drv);

return
ret;

}

下面看看看驱动和设备的匹配过程,由以前可以看出,主要是调用bus的match函数来完成匹配。

点击(此处)折叠或打开

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);

/*
Attempt an OF style match first */

if
(of_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);

}

由上面可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功。
在platform的设备驱动的编写有两种方法:

1、在bsp版文件中实现定义,在文件中将platform_device被化为一个数组,最后通过platform_add_devices函数注册。对于2440来说位于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_i2c,

&s3c_device_iis,

};

如果我们要实现一个设备的添加,那么只需要加入一个struct
platform_device的数组,然后只需要编写对应的platform_driver驱动程序就可以了。从这种方法可以看出,存在一个很明显的缺点,如果要改写驱动,就要重新的编译内核。
2、第二种方法只需要单独编写一个内核模块加载到内核中。

例子:

device文件:platform_dev.c

点击(此处)折叠或打开

struct platform_device *my_led_dev;

static int __init
platform_dev_init(void)

{

int
ret;

//分配一个
platform_device结构体

my_led_dev
=
platform_device_alloc("platform_led", -1);

ret
=
platform_device_add(my_led_dev);//将自定义的设备添加到内核设备架构中

if(ret)

platform_device_put(my_led_dev);//销毁platform设备结构

return
ret;

}

static void __exit platform_dev_exit(void)

{

platform_device_unregister(my_led_dev);//注销platform_device

}

module_init(platform_dev_init);

module_exit(platform_dev_exit);

MODULE_AUTHOR("Sola");

MODULE_LICENSE("GPL");

drv文件:platform_drv.c

点击(此处)折叠或打开

static int
s3c6410_led_open(struct inode
*inode, struct file *file)

{

unsigned
tmp;

tmp
= readl(S3C64XX_GPMCON);

tmp
= (tmp & ~(0xFFFF))|(0x1111U);

writel(tmp,
S3C64XX_GPMCON);

return
0;

}

static int
s3c6410_led_close(struct inode
*inode, struct file *file)

{

return
0;

}

static int
s3c6410_led_read(struct file
*filp, char __user *buff,
size_t count, loff_t
*offp)

{

printk("#########read######\n");

return
count;

}

static int
s3c6410_led_write (struct file
*filp, const
char __user *buf, size_t count,loff_t *f_pos)

{

char
wbuf[10];

unsigned
tmp;

copy_from_user(wbuf,buf,count);

if(wbuf[0]==1)//1号灯亮

switch(wbuf[1])

{

case
0: //off

tmp
= readl(S3C64XX_GPMDAT);

tmp
|= (0x1U);

writel(tmp,
S3C64XX_GPMDAT);

break;

case
1: //on

tmp
= readl(S3C64XX_GPMDAT);

tmp
&= ~(0x1U);

writel(tmp,
S3C64XX_GPMDAT);

break;

default
:

break;

}

if(wbuf[0]==2)//2号灯亮

switch(wbuf[1])

{

case
0: //off

tmp
= readl(S3C64XX_GPMDAT);

tmp
|= (0x2U);

writel(tmp,
S3C64XX_GPMDAT);

break;

case
1: //on

tmp
= readl(S3C64XX_GPMDAT);

tmp
&= ~(0x2U);

writel(tmp,
S3C64XX_GPMDAT);

break;

default
:

break;

}

if(wbuf[0]==3)//3号灯亮

switch(wbuf[1])

{

case
0: //off

tmp
= readl(S3C64XX_GPMDAT);

tmp
|= (0x4U);

writel(tmp,
S3C64XX_GPMDAT);

break;

case
1: //on

tmp
= readl(S3C64XX_GPMDAT);

tmp
&= ~(0x4U);

writel(tmp,
S3C64XX_GPMDAT);

break;

default
:

break;

}

if(wbuf[0]==4)//4号灯亮

switch(wbuf[1])

{

case
0: //off

tmp
= readl(S3C64XX_GPMDAT);

tmp
|= (0x8U);

writel(tmp,
S3C64XX_GPMDAT);

break;

case
1: //on

tmp
= readl(S3C64XX_GPMDAT);

tmp
&= ~(0x8U);

writel(tmp,
S3C64XX_GPMDAT);

break;

default
:

break;

}

return
count;

}

static struct file_operations led_fops = {

.owner
= THIS_MODULE,

.open
= s3c6410_led_open,

.release
= s3c6410_led_close,

.read
= s3c6410_led_read,

.write
= s3c6410_led_write,

};

static int
my_plat_probe(struct
platform_device *dev)

{

int
rc;

printk("Test
platform_led dev\n");

//注册设备

rc =
register_chrdev(LED_MAJOR,"platform_led",&led_fops);

if (rc
<</span>0)

{

printk
("register %s char dev error\n","led");

return
-1;

}

printk
("ok!\n");

return
0;

}

static int
my_plat_remove(struct
platform_device *dev)

{

printk("my
platfrom device has removed.\n");

return
0;

}

struct platform_driver my_led_drv = {

.probe
= my_plat_probe,

.remove
= my_plat_remove,

.driver
= {

.owner
= THIS_MODULE,

.name
= "platform_led",

},

};

static int __init
platform_drv_init(void)

{

int
ret;

ret
=
platform_driver_register(&my_led_drv);

return
ret;

}

static void __exit platform_drv_exit(void)

{

platform_driver_unregister(&my_led_drv);

}

module_init(platform_drv_init);

module_exit(platform_drv_exit);

MODULE_LICENSE("GPL");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: