跳到主要内容位置

平台设备驱动模板

2.写模板#

具体字符设备相关代码与硬件有关,与模板1一致。修改部分仅在引脚资源获取部分,包括设备树与平台设备注册方式。

(1)gpio_drv.c

  • 修改入口函数与出口函数:注册平台驱动程序,而原先入口函数、出口函数的工作放在platform_driver的probe和remove函数中

    static const struct of_device_id gpio_dt_ids[] = {
    { .compatible = "100ask,gpio-demo", },
    { /* sentinel */ }
    };
    static struct platform_driver gpio_platform_driver = {
    .driver = {
    .name = "100ask_gpio_plat_drv",
    .of_match_table = gpio_dt_ids,
    },
    .probe = gpio_drv_probe,
    .remove = gpio_drv_remove,
    };
    /* 在入口函数 */
    static int __init gpio_drv_init(void)
    {
    /* 注册platform_driver */
    return platform_driver_register(&gpio_platform_driver);
    }
    /* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
    */
    static void __exit gpio_drv_exit(void)
    {
    /* 注销platform_driver */
    platform_driver_unregister(&gpio_platform_driver);
    }
  • gpio_drv_probe和gpio_drv_remove,在probe函数里面获取引脚资源

    static int gpio_drv_probe(struct platform_device *pdev)
    {
    int err = 0;
    int i;
    struct device_node *np = pdev->dev.of_node;
    struct resource *res;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /* 从platform_device 获得引脚信息
    * 1. pdev来自c文件
    * 2. pdev来自设备树
    */
    if (np)
    {
    /* pdev来自设备树:
    示例
    reg_usb_ltemodule: regulator@1 {
    compatible = "100ask,gpio-demo";
    gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;
    };
    */
    count = of_gpio_count(np);
    if (!count)
    {
    return -EINVAL;
    }
    gpios = kmalloc(count*sizeof(struct gpio_desc), GFP_KERNEL);
    for ( i = 0; i < count; i++)
    {
    gpios[i].gpio = of_get_gpio(np, i);
    sprintf(gpios[i].name, "%s_pin_%d", np->name, i);
    }
    }
    else
    {
    /* pdev来自c文件
    static struct resource omap16xx_gpio3_resources[] = {
    {
    .start = 115,
    .end = 115,
    .flags = IORESOURCE_IRQ,
    },
    {
    .start = 118,
    .end = 118,
    .flags = IORESOURCE_IRQ,
    },
    };
    */
    count = 0;
    while (1)
    {
    res = platform_get_resource(pdev, IORESOURCE_IRQ, count);
    if (!res)
    {
    break;
    }
    count++;
    }
    if (!count)
    {
    return -EINVAL;
    }
    gpios = kmalloc(count*sizeof(struct gpio_desc), GFP_KERNEL);
    for ( i = 0; i < count; i++)
    {
    res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
    gpios[i].gpio = res->start;
    sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);
    }
    }
    for (i = 0; i < count; i++)
    {
    gpios[i].irq = gpio_to_irq(gpios[i].gpio); //把引脚号转换为中断号,
    setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);
    //timer_setup(&gpios[i].key_timer, key_timer_expire, 0);
    gpios[i].key_timer.expires = ~0;
    add_timer(&gpios[i].key_timer);
    //注册中断,这里按键按下与松开时就会触发中断,进入中断处理函数gpio_key_isr(gpios[i].irq, &gpios[i])
    err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    "100ask_gpio_key", &gpios[i]);
    }
    /* 注册file_operations */
    major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv); /* /dev/gpio_desc */
    gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
    if (IS_ERR(gpio_class)) {
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    unregister_chrdev(major, "100ask_gpio_key");
    return PTR_ERR(gpio_class);
    }
    device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio"); /* /dev/100ask_gpio */
    kfree(gpios);
    return err;
    }

    之前我们是通过一个数组来管理GPIO

    static struct gpio_desc gpios[] = {
    {131, 0, "gpio_100ask_1", 1,},
    {132, 0, "gpio_100ask_2", 2,},
    };

    现在是使用platform_device来获取

    1.在平台platform_device下的dev结构体中有of_node,可以从中读取gpio引脚数据(设备树在内核加载时会注册)

    struct platform_device {
    const char *name;
    int id;
    bool id_auto;
    struct device dev;
    ...
    };

    struct device dev;

    struct device {
    ...
    struct device_node *of_node; /* associated device tree node */
    ...
    };

    2.of_gpio_count(np);

    在.dts和设备驱动不关心GPIO名字的情况下,可以直接通过of_get_gpio()获取GPIO

    /**
    * of_gpio_count() - Count GPIOs for a device
    * @np: device node to count GPIOs for
    *
    * Same as of_gpio_named_count, but hard coded to use the 'gpios' property
    */
    static inline int of_gpio_count(struct device_node *np)
    {
    return of_gpio_named_count(np, "gpios"); //将设备节点中名为 “gpios” 的属性用作 GPIO 数量的计数
    }

    3.of_get_gpio(np, i);

    接受一个指向设备节点(device_node)的指针和 GPIO 的索引作为参数。

    该函数调用的实际功能由 of_get_gpio_flags 函数执行,通过设备节点和 GPIO 索引获取 GPIO 编号,并返回该编号。

    /**
    * of_get_gpio() - Get a GPIO number to use with GPIO API
    * @np: device node to get GPIO from
    * @index: index of the GPIO
    *
    * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
    * value on the error condition.
    */
    static inline int of_get_gpio(struct device_node *np, int index)
    {
    return of_get_gpio_flags(np, index, NULL);
    }

    参考《Linux设备开发详解 - 宋宝华》12.2.3 platform设备资源和数据

    使用.c文件注册platform_devic

    struct platform_device {
    const char *name;
    int id;
    bool id_auto;
    struct device dev;
    u32 num_resources;
    struct resource *resource;
    ...
    };

    可以使用IORESOURCE_IRQ表示GPIO资源,可以通过platform_get_resource获取资源

    struct resource *platform_get_resource(struct platform_device *, unsigned int,unsigned int);
    //示例
    res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
    gpios[i].gpio = res->start;

    remove函数就是之前exit函数中的工作,参数和返回值定义不同

    static int gpio_drv_remove(struct platform_device *pdev)
    {
    int i;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    device_destroy(gpio_class, MKDEV(major, 0));
    class_destroy(gpio_class);
    unregister_chrdev(major, "100ask_gpio_key");
    for (i = 0; i < count; i++)
    {
    free_irq(gpios[i].irq, &gpios[i]);
    del_timer(&gpios[i].key_timer);
    }
    return 0;
    }

(2)dts设备树文件和gpio_dev.c

示例
reg_usb_ltemodule: regulator@1 {
compatible = "regulator-fixed";
regulator-name = "ltemodule-pwr";
regulator-min-microvolt = <3800000>;
regulator-max-microvolt = <3800000>;
gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-boot-on;
};

以电机驱动为例:需要确保设备树的compatibleplatform_driver.driver.of_match_table数组中的某一项的compatible匹配

  • 修改设备树:arch/arm/boot/dts/100ask…
motor{
compatible = "100ask,gpio-demo";
gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>,
<&gpio4 20 GPIO_ACTIVE_HIGH>,
<&gpio4 21 GPIO_ACTIVE_HIGH>,
<&gpio4 22 GPIO_ACTIVE_HIGH>;
};
  • 编译:make drbs

  • 复制到开发板上

    PC:
    cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
    开发板:
    cp /mnt/100ask_imx6ull-14x14.dtb /boot/

重启开发板后可以在/sys/firmware/devicetree/base看到motor节点,进入motor节点

[root@imx6ull:/sys/firmware/devicetree/base/motor]# ls -l
total 0
-r--r--r-- 1 root root 17 Jan 1 00:11 compatible
-r--r--r-- 1 root root 48 Jan 1 00:11 gpios
-r--r--r-- 1 root root 6 Jan 1 00:11 name
[root@imx6ull:/sys/firmware/devicetree/base/motor]# cat name
motor
[root@imx6ull:/sys/firmware/devicetree/base/motor]# cat compatible
100ask,gpio-demo
[root@imx6ull:/sys/firmware/devicetree/base/motor]#

再装载驱动程序,可以看到会多出driver目录

[root@imx6ull:/sys/bus/platform/devices/motor]# ls -l
total 0
lrwxrwxrwx 1 root root 0 Jan 1 00:22 driver -> ../../../bus/platform/drivers/100ask_gpio_plat_drv
-rw-r--r-- 1 root root 4096 Jan 1 00:22 driver_override
-r--r--r-- 1 root root 4096 Jan 1 00:22 modalias
lrwxrwxrwx 1 root root 0 Jan 1 00:22 of_node -> ../../../firmware/devicetree/base/motor
drwxr-xr-x 2 root root 0 Jan 1 00:22 power
lrwxrwxrwx 1 root root 0 Jan 1 00:22 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 00:10 uevent

使用gpio_dev.c注册platform_dev

一样有入口函数和出口函数:

/* 在入口函数 */
static int __init gpio_dev_init(void)
{
/* 注册platform_device */
return platform_device_register(&gpio_platform_device);
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit gpio_dev_exit(void)
{
/* 注销platform_device */
platform_device_unregister(&gpio_platform_device);
}

定义了

static struct resource my_drv_resource[] = {
{
.flags = IORESOURCE_IRQ,
.start = 115,
.end = 115,
},
{
.flags = IORESOURCE_IRQ,
.start = 116,
.end = 116,
},
{
.flags = IORESOURCE_IRQ,
.start = 117,
.end = 117,
},
{
.flags = IORESOURCE_IRQ,
.start = 118,
.end = 118,
},
};
static struct platform_device gpio_platform_device = {
.name = "100ask_gpio_plat_drv",
.id = 0,
.num_resources = ARRAY_SIZE(my_drv_resource),
.resource = my_drv_resource,
};

这里需要gpio_platform_device.named的“100ask_gpio_plat_drv”与平台驱动的name匹配(不止一种匹配方式参考匹配方式

开发板测试gpio_drv.c和gpio_dev.c

仅装载drv

[root@imx6ull:/mnt/05_motor]# insmod gpio_drv.ko
[root@imx6ull:/sys/bus/platform/drivers]# cd 100ask_gpio_plat_drv/
[root@imx6ull:/sys/bus/platform/drivers/100ask_gpio_plat_drv]# ls
bind module uevent unbind

再装载dev

[root@imx6ull:/mnt/05_motor]# ls /sys/bus/platform/drivers/100ask_gpio_plat_drv/
100ask_gpio_plat_drv.0 module unbind
bind uevent

请点击左侧菜单(移动端为右下角)选择要查看的所有笔记吧。

最近更新于