在 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 }
匹配实例:以触摸屏驱动分析;
平台设备部分:
平台设备列表:
将设备结构s3c_device_ts 注册进内核:
平台驱动部分;
int platform_add_devices(struct platform_device **devs, int num);
该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它内部调用了platform_device_register()函数用于注册单个的平台设备
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 };
(1)使得设备被挂接在一个总线上,因此,符合Linux 2.6 的设备模型。其结果是,配套的sysfs 结点、设备电源管理都成为可能。
( 2)隔离 BSP 和驱动。在 BSP 中定义 platform 设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API 去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
( 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()