1、平台总线模型
平台总线模型是Linux系统虚拟出来的总线,而I2C、SPI等物理总线是真实存在的。
平台总线模型将一个驱动分成两个部分,分别是device.c和driver.c,分别用来描述硬件信息和控制硬件。
平台总线通过字符串比较,将name相同的device.c和driver.c匹配到一起来控制硬件。
平台总线模型的优点:
- 减少编写重复代码,提高效率
- 提高代码的利用率
2、platform device
//\Linux-4.9.88\include\linux\platform_device.h
struct platform_device {
const char *name;//设备名
int id; //设备ID号
bool id_auto;
struct device dev; //包含一个具体的device结构体
u32 num_resources; //资源的数量
struct resource *resource; //用来保存硬件资源的结构体 io资源,中断资源,内存资源
const struct platform_device_id *id_entry; //平台设备的id
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell; //用户多功能卡,多功能设备的实现
/* arch specific additions */
struct pdev_archdata archdata;
};
extern int platform_device_register(struct platform_device *); //注册平台总线设备
extern void platform_device_unregister(struct platform_device *);//删除平台总线设备
2.1、struct device
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
void *driver_data; /* Driver data, set and get with
dev_set/get_drvdata */
struct dev_links_info links;
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
struct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
struct list_head msi_list;
#endif
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
unsigned long dma_pfn_offset;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
struct iommu_fwspec *iommu_fwspec;
bool offline_disabled:1;
bool offline:1;
};
3、platform 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; //内嵌了一个设备驱动结构体
/*平台设备ID,这与platform_device中的struct platform_device_id *id_entry是相同的
主要是完成总线的匹配操作,platform总线的匹配操作第一匹配要素就是该元素。而不再是简单的name选项*/
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
3.1、 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);
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 dev_pm_ops *pm;
struct driver_private *p;
};
4、实验demo
4.1 platform_device.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
//static struct resource * res;
static void platform_dev_release(struct device *dev)
{
printk("this is a test for platform_device\n");
}
static struct resource platform_dev_resource[] = {
};
struct platform_device platform_dev_test = {
.name = "100ask_test",
.num_resources = ARRAY_SIZE(platform_dev_resource),
.id = -1,
.resource = platform_dev_resource,
.dev = {
.release = platform_dev_release,
},
};
static int __init platform_dev_test_init(void)
{
int err;
err = platform_device_register(&platform_dev_test);
return 0;
}
static void __exit platform_dev_test_exit(void)
{
platform_device_unregister(&platform_dev_test);
}
module_init(platform_dev_test_init);
module_exit(platform_dev_test_exit);
MODULE_LICENSE("GPL");
4.2 platform_driver.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
static int platform_drv_probe(struct platform_device *pdev)
{
printk("this is a test for platform_drv\n");
return 0;
}
static int platform_drv_remove(struct platform_device *pdev)
{
printk("this is in platform_drv_remove\n");
return 0;
}
static struct platform_driver platform_drv_test = {
.probe = platform_drv_probe,
.remove = platform_drv_remove,
.driver = {
.name = "100ask_test",
},
};
static int __init platform_drv_test_init(void)
{
int err;
err = platform_driver_register(&platform_drv_test);
return 0;
}
static void __exit platform_drv_test_exit(void)
{
platform_driver_unregister(&platform_drv_test);
}
module_init(platform_drv_test_init);
module_exit(platform_drv_test_exit);
MODULE_LICENSE("GPL");
4.3 Makefile
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
KERN_DIR = /home/johan/share/linux_bsp/100ask_imx6ull-sdk/Linux-4.9.88
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
#编译成ko
platform_dev-y := platform_device.o
platform_drv-y := platform_driver.o
obj-m += platform_dev.o
obj-m += platform_drv.o
4.4 板子上测试
加载驱动
匹配成功,调用prode函数
5、总结
总的来说,主要是填充两个结构体,struct platform_device和struct platform_driver