(三)编译设备树:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs
编译生成的设备树文件为imx6ull-elf1-emmc.dtb,参考《01-0 ELF1、ELF1S开发板_快速启动手册_V1》4.4节单独更新设备树。
驱动源码platform_led.c编写
(一)头文件引用
#include
#include // 包含模块相关函数的头文件
#include // 包含文件系统相关函数的头文件
#include
#include
#include //包含设备节点相关的头文件
#include //包含gpio操作函数的相关头文件
#include
#include
(二)创建相关宏定义
#define DEVICE_NAME "my_device"
#define LED_IOC_MAGIC 'k'
#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)
#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)
struct device_node *node; //设备树节点
int gpio; //gpio编号
这里同样使用ioctl来控制,ioctl相关的定义本节不在重复讲解。
(三)定义platform_driver类型结构体
static struct platform_driver my_platform_driver = {
.driver = {
.name = "my_platform_driver",
.owner = THIS_MODULE,
.of_match_table = of_platform_match,
},
.probe = my_platform_probe,
.remove = my_platform_remove,
};
(四)定义of_platform_match,用来与设备树中的compatible匹配,匹配成功后才会进入到probe函数中
static const struct of_device_id of_platform_match[] = {
{ .compatible = "platform_led", },
{},
};
(五)probe函数的实现
static int my_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret;
//注册杂项设备
ret = misc_register(&my_misc_device);
if (ret) {
pr_err("Failed to register misc device\n");
return ret;
}
// 获取设备节点
node = of_find_node_by_name(NULL,"leds");
// 获取 gpios 属性中的引脚编号
gpio = of_get_named_gpio(node, "gpios", 0);
//判断是否获取成功
if (!gpio_is_valid(gpio)) {
pr_err("Failed to get GPIO\n");
return -1;
}
gpio_free(gpio);
if (gpio_request(gpio, "led_run")) {
printk("request %s gpio faile \n", "led_run");
return -1;
}
printk(KERN_INFO "my_platform_probe: Platform device probed\n");
return 0;
}
(1)misc_register()用户注册杂项设备,原型如下:
int misc_register(struct miscdevice *misc);
参数说明:
misc:指向struct miscdevice结构体的指针,表示要注册的杂项设备。
返回值是一个整数,表示注册结果。如果注册成功,函数会返回0;如果注册失败,函数会返回一个负数,表示错误代码。
struct miscdevice结构体用于描述杂项设备的属性,包括设备的次设备号(minor number)、设备名称、设备文件操作函数等。在注册杂项设备之前,需要先初始化struct miscdevice 结构体的字段,然后将其作为参数传递给 misc_register()函数。
(2)of_find_node_by_name()用于查找设备树中的节点,原型如下:
struct device_node *of_find_node_by_name(struct device_node *prev, const char *name);
参数说明:
prev:指向前一个设备树节点的指针。如果要从整个设备树中开始查找节点,则可以传递NULL。
name:字符串,表示要查找的设备树节点的名称。
返回值是一个指向找到的设备树节点的指针。如果找到匹配的节点,则返回该节点的指针;如果未找到匹配的节点,则返回 NULL。
(3)of_get_named_gpio()用于从设备树中获取GPIO引脚编号,原型如下:
int of_get_named_gpio(const struct device_node *np, const char *propname, int index);
参数说明:
np:指向设备树节点的指针,表示要从该节点获取 GPIO 引脚。
propname:字符串,表示要获取的 GPIO 引脚属性的名称。
index:整数,表示在属性中指定的 GPIO 引脚的索引。如果属性中有多个 GPIO 引脚定义,可以通过指定索引来获取其中的一个引脚。
返回值是一个整数,表示获取到的 GPIO 引脚编号。如果获取失败,函数会返回一个负数。
(六)miscdevice类型结构体定义
static struct miscdevice my_misc_device = {
.minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号
.name = DEVICE_NAME, // 设备名称
.fops = &my_device_fops, // 设备文件操作函数
};
(七)文件操作函数,file_operations类型结构体定义
static struct file_operations my_device_fops = {
.owner = THIS_MODULE,
.open = my_device_open,
.release = my_device_release,
.unlocked_ioctl = myled_ioctl,
};
(八)文件操作函数实现:
static int my_device_open(struct inode *inode, struct file *file)
{
// 打开设备的操作
//将引脚配置为输出
gpio_direction_output(gpio, 1);
printk(KERN_INFO "This is device_open.\n");
return 0;
}
static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case SET_LED_ON:
printk(KERN_INFO "This is set_led_on.\n");
// 设置GPIO引脚为低电平
gpio_set_value(gpio, 0);
break;
case SET_LED_OFF:
//设置GPIO引脚为高电平
printk(KERN_INFO "This is set_led_off.\n");
gpio_set_value(gpio, 1);
break;
default:
return -ENOTTY;
}
return 0;
}
static int my_device_release(struct inode *inode, struct file *file)
{
// 关闭设备的操作
return 0;
}
文件操作函数同样使用ioctl来实现,与之前的LED操作一样,接收到SET_LED_ON命令后将引脚拉低,接收到SET_LED_OFF命令后,将引脚电平拉高。
完整的驱动platform_led.c示例源码
#include
#include // 包含模块相关函数的头文件
#include // 包含文件系统相关函数的头文件
#include
#include
#include //包含设备节点相关的头文件
#include //包含gpio操作函数的相关头文件
#include
#include
#define DEVICE_NAME "my_device"
#define LED_IOC_MAGIC 'k'
#define SET_LED_ON _IO(LED_IOC_MAGIC, 0)
#define SET_LED_OFF _IO(LED_IOC_MAGIC, 1)
struct device_node *node; //设备树节点
int gpio; //gpio编号
static int my_device_open(struct inode *inode, struct file *file)
{
// 打开设备的操作
//将引脚配置为输出
gpio_direction_output(gpio, 1);
printk(KERN_INFO "This is device_open.\n");
return 0;
}
static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case SET_LED_ON:
printk(KERN_INFO "This is set_led_on.\n");
// 设置GPIO引脚为低电平
gpio_set_value(gpio, 0);
break;
case SET_LED_OFF:
//设置GPIO引脚为高电平
printk(KERN_INFO "This is set_led_off.\n");
gpio_set_value(gpio, 1);
break;
default:
return -ENOTTY;
}
return 0;
}
static int my_device_release(struct inode *inode, struct file *file)
{
// 关闭设备的操作
return 0;
}
static struct file_operations my_device_fops = {
.owner = THIS_MODULE,
.open = my_device_open,
.release = my_device_release,
.unlocked_ioctl = myled_ioctl,
};
static struct miscdevice my_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_device_fops,
};
static int my_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret;
//注册杂项设备
ret = misc_register(&my_misc_device);
if (ret) {
pr_err("Failed to register misc device\n");
return ret;
}
// 获取设备节点
node = of_find_node_by_name(NULL,"leds");
// 获取 gpios 属性中的引脚编号
gpio = of_get_named_gpio(node, "gpios", 0);
//判断是否获取成功
if (!gpio_is_valid(gpio)) {
pr_err("Failed to get GPIO\n");
return -1;
}
gpio_free(gpio);
if (gpio_request(gpio, "led_run")) {
printk("request %s gpio faile \n", "led_run");
return -1;
}
printk(KERN_INFO "my_platform_probe: Platform device probed\n");
return 0;
}
static int my_platform_remove(struct platform_device *pdev)
{
misc_deregister(&my_misc_device);
printk(KERN_INFO "my_platform_remove: Platform device removed\n");
return 0;
}
static const struct of_device_id of_platform_match[] = {
{ .compatible = "platform_led", },
{},
};
static struct platform_driver my_platform_driver = {
.driver = {
.name = "my_platform_driver",
.owner = THIS_MODULE,
.of_match_table = of_platform_match,
},
.probe = my_platform_probe,
.remove = my_platform_remove,
};
module_platform_driver(my_platform_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Platform Driver Example");
编译
复制7.7.2驱动中的Makefile文件,将其中的platform.o修改为platform_led.o,效果如下:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
elf@ubuntu:~/work/test/06_platform/platform_led$ make
将编译生成的platform_led.ko模块拷贝到开发板。
测试
测试app使用之前章节的app即可。
root@ELF1:~# insmod platform_led.ko
my_platform_probe: Platform device probed
root@ELF1:~# ./led_app
This is device_open.
This is set_led_on.
This is set_led_off.
This is set_led_on.
This is set_led_off.
This is set_led_on.
This is set_led_off.
This is set_led_on.
This is set_led_off.
This is set_led_on.
This is set_led_off.
root@ELF1:~# rmmod platform_led.ko
my_platform_remove: Platform device removed
此时可以看到开发板上的红色LED,循环闪烁5次。