总线,驱动,设备
要满足linux设备模型,就需要有对应总线(struct bus_type),驱动(struct device_driver),设备(struct device)。
具体的总线类型可看/sys/bus/下,常见的spi,i2c,usb等总线;
但是有的设备并没有对应的物理总线,如RTC,LED,按键等.为此内核专门开发了一种虚拟总线---platform总线;
用于连接一些没有物理总线或不支持热插拔的设备;
通常总线在内核中已经实现好了,我们只需要写对应总线驱动,有时还需要写相应的设备注册代码;
平台总线
在内核driver/base/platform.c下有platform总线定义;
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
内核自己实现好了platform总线,对应/sys/bus/platform;
当然,也可以自己使用struct bus_type实现定制总线,通过bus_register(&bus)注册;
struct bus_type
struct bus_type {
const char *name;//bus名字
const char *dev_name;//用于子系统枚举设备,如foo%u,dev->id
struct device *dev_root;//设备默认将其作为父对象
const struct attribute_group **bus_groups;//bus默认属性
const struct attribute_group **dev_groups;//dev默认属性
const struct attribute_group **drv_groups;//drv默认属性
//每当为该总线添加新设备或驱动程序时,被调用;匹配成功返回正值,否则返回0;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
//当新设备或驱动程序添加到这个总线时被调用,并在其中回调驱动的probe来初始化匹配的设备;
int (*probe)(struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove)(struct device *dev);//当device从该总线移除时调用
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);//电源挂起调用
int (*resume)(struct device *dev);//电源恢复调用
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;//驱动核心私有数据,仅仅驱动核能见;
struct lock_class_key lock_key;
bool need_parent_lock;
};
平台驱动
基本的设备驱动结构体:struct device_driver
struct device_driver {
const char *name;//设备驱动名
struct bus_type *bus;//这个驱动属于的设备的总线;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table;//打开的固件表
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct attribute_group **dev_groups;
const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);
struct driver_private *p;//驱动核私有数据
};
平台设备驱动:struct platform_driver
// include <linux/driver.h>
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;//内嵌struct device_dirver
const struct platform_device_id *id_table;//平台驱动可以驱动的平台设备ID列表;
bool prevent_deferred_probe;
};
向平台总线注册注销平台驱动;
int platform_driver_register(struct platform_driver *pdrv);
void platform_driver_unregister(struct platform_driver *pdrv);
平台设备
//#include <linux/platform_device.h>
struct platform_device {
const char *name;//平台设备名,在平台总线的match中可用于同平台驱动匹配;
int id;//设备的id号,用于区别同类型的不同平台设备.(pdev%d,id)
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;//平台设备使用的资源个数;
struct resource *resource;//平台设备的资源列表,指向资源的首地址;
//用于平台驱动匹配的ID,在平台总线的match函数中,首先尝试匹配该ID,如果不成功,在尝试用name匹配;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
平台设备最主要的就是struct resource,它是实现设备与驱动分量的关键;
//#include <linux/ioport.h>
struct resource {
//资源的开始,对于IO内存来说是起始的内存地址;对于中断资源来说就是起始中断号;对于DMA来说就是起始的DMA通道号;
resource_size_t start;
resource_size_t end;//资源的结束
const char *name;
//资源的标志;IORESOURCE_MEM资源的类型时内存资源; IORESOURCE_IRQ:中断资源 IORESOURCE_DMA:DMA通道资源;
unsigned long flags;
unsigned long desc;
struct resource *parent, *sibling, *child;
};
向平台总线注册或注销平台设备:
int platform_add_devices(struct platform_device **devs, int num);//一次可以注册num个
int platform_device_register(struct platform_device *pdev);//只能注册一个
void platform_device_unregister(struct platform_device *pdev);
关键:当平台总线发现有和平台设备匹配的驱动时,就会调用平台驱动内的probe函数,并传递匹配的平台设备结构地址;
平台驱动就可以获得设备资源;
获取资源的函数:
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
resource_size_t resource_size(const struct resource *res);
定义资源
struct resource res[] = {
[0] = {
.start = 0x10000000,//物理地址 开始
.end = 0x10000000+100,//结束
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0x100,//中断号 中断号有限 只能start即可
.flags = IORESOURCE_IRQ,
},
//用宏定义
[2] = DEFINE_RES_MEM(0x20000000,0x100),
[3] = DEFINE_RES_IRQ(0x200),
};
示例
平台驱动;
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
static int pdrv_probe(struct platform_device *dev)
{
printk("-----pdrv_probe----\n");
return 0;
}
static int pdrv_remove(struct platform_device *dev)
{
printk("-----pdrv_remove----\n");
return 0;
}
static int drv_suspend(struct device *dev)
{
printk("-----drv_suspend----\n");
return 0;
}
static int drv_resume(struct device *dev)
{
printk("-----drv_resume----\n");
return 0;
}
static struct dev_pm_ops drv_pm_opt = {
.suspend = drv_suspend,
.resume = drv_resume,
};
static struct platform_driver pltdrv = {
.driver = {
.name = "pltdev",
.owner = THIS_MODULE,
.pm = &drv_pm_opt,
},
.probe = pdrv_probe,
.remove = pdrv_remove,
};
static __init int pltdrv_init(void)
{
platform_driver_register(&pltdrv);
return 0;
}
static void __exit pltdrv_exit(void)
{
platform_driver_unregister(&pltdrv);
}
module_init(pltdrv_init);
module_exit(pltdrv_exit);
MODULE_LICENSE("GPL");
平台设备;
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
static void pdev_release(struct device *dev)
{
}
static struct platform_device pltdev0 = {
.name = "pltdev",
.id = 0,
.num_resources = 0,
.resource = NULL,
.dev = {
.release = pdev_release,
},
};
static struct platform_device pltdev1 = {
.name = "pltdev",
.id = 1,
.num_resources = 0,
.resource = NULL,
.dev = {
.release = pdev_release,
},
};
static __init int pltdev_init(void)
{
platform_device_register(&pltdev0);
platform_device_register(&pltdev1);
return 0;
}
static void __exit pltdev_exit(void)
{
platform_device_unregister(&pltdev0);
platform_device_unregister(&pltdev1);
}
module_init(pltdev_init);
module_exit(pltdev_exit);
MODULE_LICENSE("GPL");