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; //平台设备的idchar *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 thisdevice */void *platform_data; /* Platform specific data, devicecore doesn't touch it */void *driver_data; /* Driver data, set and get withdev_set/get_drvdata */struct dev_links_info links;struct dev_pm_info power;struct dev_pm_domain *pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head msi_list;
#endif#ifdef CONFIG_NUMAint numa_node; /* NUMA node this device is close to */
#endifu64 *dma_mask; /* dma mask (if dma'able device) */u64 coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations 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 memoverride */
#ifdef CONFIG_DMA_CMAstruct cma *cma_area; /* contiguous memory area for dmaallocations */
#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.88all: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