Linux从2.6起加入了一套新的驱动管理和注册的机制。platform平台总线是一条虚拟的总线,设备用platform_device表示,驱动用platform_driver进行注册。与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性。
下面来看看内核注册platform总线的过程。
platform总线struct bus_typeplatform_bus_type
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设备struct device platform_bus
struct deviceplatform_bus = {
.init_name = "platform",
};
平台总线初始化
其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为
1、定义一个platform_device,并注册;
2、定义一个platform_driver,并注册;
3、通过platform_match成员函数来确定platform_device与platform_driver如何匹配。
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 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结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型、flags可以用内存资源、IO资源、中断资源等。
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
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注册
其最后也是调用device_add的,主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程。
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 < 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 toclaim resource %d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registeringplatform 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;
}
struct platform_driver
platform_driver包含了设备操作的功能函数,同时包含了device_driver结构。
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;
};
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函数来完成匹配。可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功
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 idtable */
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);
}
在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 __initplatform_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 __exitplatform_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_operationsled_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 <0)
{
printk ("register %schar dev error\n","led");
return -1;
}
printk ("ok!\n");
return 0;
}
static int my_plat_remove(struct platform_device *dev)
{
printk("my platfromdevice has removed.\n");
return 0;
}
structplatform_driver my_led_drv = {
.probe = my_plat_probe,
.remove = my_plat_remove,
.driver = {
.owner = THIS_MODULE,
.name = "platform_led",
},
};
static int __initplatform_drv_init(void)
{
int ret;
ret = platform_driver_register(&my_led_drv);
return ret;
}
static void __exitplatform_drv_exit(void)
{
platform_driver_unregister(&my_led_drv);
}
module_init(platform_drv_init);
module_exit(platform_drv_exit);
MODULE_LICENSE("GPL");