Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
---------------------------------------引子网友的帖子 ,忘了连接。当时学习的时候看了他的文章
platform_device结构体用来描述设备的名称、资源信息等。
这里分析在dm9000中的platform的调用流程
1、dm9000 的probe 函数中
static int
dm9000_probe(struct platform_device *pdev)
{
struct net_device *ndev;
ndev = alloc_etherdev(sizeof (struct board_info));
/*
* ether_setup针对以太网卡的的函数,用来填充剩下的函数指针(内核已经实现好的)
* 前提是你是以太网卡
*
会自动填充init函数
*/ 会自动填充 init函数
ether_setup(ndev);
}
而init的函数定义如下
static int __init
dm9000_init(void)
{
printk(KERN_INFO "%s Ethernet Driver\n", CARDNAME);
/*函数使用的参数看下文*/
return platform_driver_register(&
dm9000_drive
r);
/* search board and register */
}
这里是 platform_driver_register中传入的参数的定义
先了解 数据类型的定义:
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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver; // 在platfrom_driver注册的时候 实际注册的是这个驱动
};
static struct platform_driver dm9000_driver =
{
// 说明应该有个设备 名字也应该是dm9000
.driver = { .name = "dm9000", .owner = THIS_MODULE, },
/*
* 认真看会发现
dm9000_probe的参数是
struct platform_device *pdev
* 所以这个probe 函数是由这个机制调用的
.probe = dm9000_probe,
.remove = dm9000_drv_remove,
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
};
但是定义驱动之前是不是应该有设备呢 ? 所以还要有设备驱动的定义:
首先先了解下 platfrom 设备是如何定义的:
struct platform_device
{
const char * name;
//设备名字 设备的名字,这将代替device->dev_id,用作sys/device下显示的目录名
int id; //用于给插入到该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。
struct device dev; //结构体中内嵌的device结构体。
u32 num_resources; //资源数
struct resource * resource; //资源
};
static struct platform_device s3c_device_dm9k
=
{
.name = "dm9000",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_dm9k_resource),
.resource = s3c_dm9k_resource,
.dev = {
.platform_data = &s3c_dm9k_platdata,
}
};
以上添加了两个变量第一个是
s3c_dm9k_resource
platfrom 机制的变量都有统一的描述。其数据结构我将在下面给出:
static struct
resource
s3c_dm9k_resource[] =
{
[0] = {
.start = S3C2410_CS4,
/* ADDR2=0,发送地址时使用这个地址 */
.end = S3C2410_CS4 + 3,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S3C2410_CS4 + 4,
/* ADDR2=1,传输数据时使用这个地址 */
.end = S3C2410_CS4 + 4 + 3,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_EINT7,
/* 中断号 */
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ,
}
};
static struct
dm9000_plat_data
s3c_dm9k_platdata
=
{
.flags = DM9000_PLATF_16BITONLY, //由此可知dm9000使用的是16位的传输模式
};
struct dm9000_plat_data
{
unsigned int flags; /* allow replacement IO routines */
void (*inblk)(void __iomem *reg, void *data, int len);
void (*outblk)(void __iomem *reg, void *data, int len);
void (*dumpblk)(void __iomem *reg, int len);
};
2、我们跟着进入
platform_driver_register 函数一看:
其传入的参数为:
struct
platform_driver,
dm9000_driver 并且我们队其中的函数指针做了初始化。(该变量的定义是在 驱动程序里面,所以才可以对使用存在的函数来初始化 paltform_driver变量里面的函数指针)
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //系统实现好的
if (drv->probe) //probe存在在定义的时候已经初始化了 所以这里是真
drv->driver.probe = platform_drv_probe; // 从driver的定义可以里面内嵌了一个device变量
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
platform_drv_probe函数代码如下:
static int platform_drv_probe(struct device *_dev)
{
/*
*
#define to_platform_driver(drv)
(container_of((drv), struct platform_driver,
driver))
*
将一般的driver结构体封装或者是获得platform_driver类型的结构体指针,这个显然是根据driver指针算出相应的 * platform_driver指针
*/
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev); //接着调用上面赋值的 probe函数。
}