[Linux内核驱动]platform

platform设备驱动

这里只是简单介绍,更详细的内容在bus/platform/中。

Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,为了达到所有硬件都可以按照总线设备驱动模型来实现驱动,Linux从2.6起就加入了 platform 设备驱动,在内核中建立一条虚拟的总线platform,它可以将那些不依赖于传统总线(如PCI, USB, I2C等)的设备,虚拟的挂在了platform总线上,达到统一。

platform_device结构体

struct platform_device
{
 	// 设备的名字,用于和驱动进行匹配的
	const char *name;
	// 内核中维护的所有的设备必须包含该成员
    struct device dev;
	//资源个数
    u32 num_resources;	
    //描述资源
    struct resource * resource;
    ...
};

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;  
    const struct platform_device_id *id_table;  
    bool prevent_deferred_probe;
};

  • probe: 当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中
  • remove: 当设备被移除时,此函数被调用。
  • shutdown: 系统关闭时,此函数被调用。
  • suspend 和 resume: 电源管理相关的回调,用于设备挂起和恢复。
  • driver: 这是一个 struct device_driver 结构体,包含了驱动的一些通用信息。
  • id_table: 往往一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该结构体数组中。

platform_bus_type

系统为platform总线创建了一个总线类型,platform_bus_type定义位于Linux/drivers/base/platform.c

struct bus_type platform_bus_type = {
        .name           = "platform",
        .dev_groups     = platform_dev_groups,
        .match          = platform_match,
        .uevent         = platform_uevent,
        .pm             = &platform_dev_pm_ops,
};

其中的match()函数实现了platform_device和platform_driver之间的匹配。

测试

make
insmod platform.ko
insmod platform-dev.ko

成功后可以看到如下目录结构

➜  my_platform_device git:(master)pwd
/sys/devices/platform/my_platform_device
➜  my_platform_device git:(master) ✗ ll
total 0
-rw-r--r-- 1 root root 4.0K  519 15:56 driver_override
-r--r--r-- 1 root root 4.0K  519 15:56 modalias
drwxr-xr-x 2 root root    0  519 15:56 power
lrwxrwxrwx 1 root root    0  519 15:56 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4.0K  519 15:56 uevent

其他

关于platform的知识还有很多,这里只是简单的介绍和一种编写的方法,更多的知识在网上查阅,也可以参考内核中一些设备的实现,如DM9000网卡驱动(/linux/drivers/net/ethernet/davicom/dm9000.c )等

代码

platform-dev.c

#include <linux/module.h>
#include <linux/platform_device.h>

static struct platform_device* pdev;

static int __init platform_init_module(void)
{
  int ret = 0;

  // platform_device_alloc 函数
  // 用于分配一个新的 platform_device 结构体,并初始化其中的一些字段。
  // 这个函数不会将设备添加到系统中,只是简单地分配并初始化结构体。
  pdev = platform_device_alloc("my_platform_device", -1);
  if (!pdev)
    return -ENOMEM;

  // platform_device_add 函数
  // 用于将之前通过 platform_device_alloc 分配并初始化
  // 的 platform_device 结构体添加到系统中。
  // 这个函数会触发与设备相关的驱动程序进行匹配,
  // 并可能触发设备的探测和初始化过程。
  ret = platform_device_add(pdev);
  if (ret)
  {
    // platform_device_put 函数用于释放通过 platform_device_alloc 分配的平台设备。
    // 这个函数会递减设备的引用计数,并在引用计数达到 0 时释放设备占用的内存。
    // 如果设备已经被添加到系统中(通过 platform_device_add),
    // 那么你需要先使用 platform_device_unregister 来注销设备,
    // 然后才能使用 platform_device_put 来释放它
    platform_device_put(pdev);
    return ret;
  }
  printk(KERN_INFO "platform_init_module \n");
  return 0;
}
static void __exit platform_exit_module(void)
{
  printk(KERN_INFO "platform_exit_module \n");
  platform_device_unregister(pdev);
  platform_device_put(pdev);
}

module_init(platform_init_module);
module_exit(platform_exit_module);

MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");

platform.c

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

// miscdevice结构体在Linux内核中用于定义杂项设备(也称为混合设备或misc设备)的属性。
// 杂项设备是字符设备的一种,当板子上的某个设备没有办法明确分类时,
// 可以使用杂项设备驱动。杂项设备可以自动生成设备节点,
// 并且所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号。
struct miscdevice misc_device;

static int platform_device_open_func(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "platform_open_func\n");
    return 0;
}
static int platform_device_release_func(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "platform_release_func\n");
    return 0;
}
static const struct file_operations platform_device_fops = {
    .owner = THIS_MODULE,
    .open = platform_device_open_func,
    .release = platform_device_release_func,
};

static int platform_driver_probe_func(struct platform_device *pdev)
{
    int ret;
    // 创建设备节点
    misc_device.minor = MISC_DYNAMIC_MINOR;
    misc_device.name = "platform_device";
    misc_device.fops = &platform_device_fops;

    ret = misc_register(&misc_device);
    if (ret < 0)
    {
        printk(KERN_ERR "Failed to register misc device\n");
        return ret;
    }

    // dev_info 是一个宏,通常用于在内核驱动中打印信息。
    dev_info(&pdev->dev, "Platform device probed\n");

    printk(KERN_INFO "Platform device probed\n");
    return 0;
}

static int platform_driver_remove_func(struct platform_device *pdev)
{
    printk(KERN_INFO "Platform device removed\n");
    misc_deregister(&misc_device);
    dev_info(&pdev->dev, "Platform device removed\n");
    return 0;
}

static struct platform_driver platform_driver = {
    .driver = {
        .name = "platform_device",
        .owner = THIS_MODULE,
    },
    .probe = platform_driver_probe_func,
    .remove = platform_driver_remove_func,
};

module_platform_driver(platform_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lidonghang-02");

Makefile

KVERS = $(shell uname -r)

# Kernel modules
obj-m += platform-dev.o platform.o

# Specify flags for the module compilation.
EXTRA_CFLAGS=-g -O0

build: kernel_modules

kernel_modules:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules

clean:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值