参考:
正点原子Linux第五十四章 platform设备驱动实验
一张图掌握 Linux platform 平台设备驱动框架
上一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统)
下一篇:更新中…
概述
platform是一种分层思想,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动;platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。
-
初学对具体设计的意图无需完全掌握,大致掌握运行流,在内核驱动中可以找到和读懂相关代码即可,用户编写驱动可以参考模板
-
参考下图,一图流,完全弄懂这张图就能完全掌握platform框架的思想 😃
图源:一张图掌握 Linux platform 平台设备驱动框架!
设备树修改
设备树直接使用上一章即可,无需修改
简要分析
在上一章源码基础上修改,添加platform相关的数据结构和匹配表,实现led_probe(即原驱动初始化函数),实现led_remove(原退出函数),并将驱动初始化改为platform_driver_register和platform_driver_unregister
//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver = {.name = "platform-led", // 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table = led_of_match, // 设备树匹配表},.probe = led_probe,.remove = led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}
如上,设备树与led_of_match的属性值匹配后,即执行led_probe函数,同理:卸载驱动时会执行led_remove函数,卸载操作无需改动
函数原型:int (*probe)(struct platform_device *);
led_probe函数参考实现:
static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green"); // 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);...// 注册字符设备...}
这里直接使用pdev->dev.of_node
引用设备节点即可
后续注册字符设备、操作函数ops等无改动,不再赘述
测试
chardevApp同样使用上一章的测试APP,功能正常实现
源码
platform_led.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>struct platform_led_dev
{dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;int led_gpio;
};
struct platform_led_dev platform_led = {.major = 0,
};#define PIN_N 0 // 第0个引脚,PG0,绿色
#define DEV_NAME "platform_led"
#define LED_ON 0 // 上拉,低电平亮
#define LED_OFF 1static int led_gpio_open(struct inode *inode, struct file *file)
{file->private_data = &platform_led;return 0;
}static int led_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{return 0;
}static int led_gpio_release(struct inode *inode, struct file *file)
{return 0;
}static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{int ret = 0;unsigned char databuf;struct platform_led_dev *dev = file->private_data;ret = copy_from_user(&databuf, user_buf, sizeof(databuf));if (ret < 0){pr_err("copy_from_user failed\r\n");return -EFAULT;}if (databuf == 0 || databuf == '0') // LED_OFFgpio_set_value(dev->led_gpio, 1);if (databuf == 1 || databuf == '1') // LED_ONgpio_set_value(dev->led_gpio, 0);return 1;
}static const struct file_operations platform_led_fops = {.open = led_gpio_open,.read = led_gpio_read,.release = led_gpio_release,.write = led_gpio_write,
};static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green");if (ret){printk(KERN_ERR "Failed to request gpio\n");return ret;}// 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);if (ret < 0){printk("can't set gpio!\r\n");}// 注册字符设备if (platform_led.major) // 定义了设备号,静态设备号{platform_led.devid = MKDEV(platform_led.major, 0);ret = register_chrdev_region(platform_led.major, 1, DEV_NAME);if (ret < 0){pr_err("cannot register %s char driver.ret:%d\r\n", DEV_NAME, ret);goto exit;}}else // 没有定义设备号,动态申请设备号{ret = alloc_chrdev_region(&platform_led.devid, 0, 1, DEV_NAME);if (ret < 0){pr_err("cannot alloc_chrdev_region,ret:%d\r\n", ret);goto exit;}platform_led.major = MAJOR(platform_led.devid);platform_led.minor = MINOR(platform_led.devid);}printk("led major=%d,minor=%d\r\n", platform_led.major, platform_led.minor);platform_led.cdev.owner = THIS_MODULE;cdev_init(&platform_led.cdev, &platform_led_fops);ret = cdev_add(&platform_led.cdev, platform_led.devid, 1);if (ret < 0)goto del_unregister;platform_led.class = class_create(THIS_MODULE, DEV_NAME);if (IS_ERR(platform_led.class))goto del_cdev;platform_led.device = device_create(platform_led.class, NULL, platform_led.devid, NULL, DEV_NAME);if (IS_ERR(platform_led.device))goto destroy_class;return 0;// 注意 goto后的标签没有return操作,将顺序执行多个label直至return,这里反向写
destroy_class:class_destroy(platform_led.class);
del_cdev:cdev_del(&platform_led.cdev);
del_unregister:unregister_chrdev_region(platform_led.devid, 1);
exit:printk("init failed\r\n");return -EIO;
}static int led_remove(struct platform_device *pdev)
{if (platform_led.led_gpio){gpio_set_value(platform_led.led_gpio, 1);gpio_free(platform_led.led_gpio);}cdev_del(&platform_led.cdev);unregister_chrdev_region(platform_led.devid, 1);device_destroy(platform_led.class, platform_led.devid);class_destroy(platform_led.class);return 0;
}//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver = {.name = "platform-led", // 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table = led_of_match, // 设备树匹配表},.probe = led_probe,.remove = led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("USER");
MODULE_INFO(intree, "Y");