1.添加设备树子节点
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xa.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxac-pinctrl.dtsi"
#include "stm32mp15xx-fsmp1x.dtsi"
/ {
model = "HQYJ STM32MP157 FSMP1A Discovery Board";
compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
aliases {
serial0 = &uart4;
serial5 = &usart3;
};
chosen {
stdout-path = "serial0:115200n8";
};
reserved-memory {
gpu_reserved: gpu@d4000000 {
reg = <0xd4000000 0x4000000>;
no-map;
};
optee_memory: optee@0xde000000 {
reg = <0xde000000 0x02000000>;
no-map;
};
};
mynode@0x12345678{
astring="hello 21091";
uint =<0xaabbccdd 0x11223344>;
binarry=[00 0c 29 7b f9 be];
mixed ="hello",[11 22],<0x12345678>;
};
myled{
led1-gpio=<&gpioe 10 0>;
led2-gpio=<&gpiof 10 0>;
led3-gpio=<&gpioe 8 0>;
};
};
2.编写驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
// 主设备号
unsigned int major;
struct class *cls;
struct device *dev;
// 宏定命令码
#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)
struct device_node *dnode;
struct gpio_desc *gpiono[3];
int myled_open(struct inode *inode, struct file *file)
{
// 判断次设备号
int min = MINOR(inode->i_rdev);
printk("min = %d\n",min);
// 填充进file->private
file->private_data = (void *)min;
return 0;
}
int myled_close(struct inode *inode, struct file *file)
{
return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int min = (int)file->private_data;
switch (min)
{
case 0:
switch (cmd)
{
case LED_ON:
// 灯亮
printk("这是一个调试信息 %d \n", __LINE__);
gpiod_set_value(gpiono[0], 1);
printk("这是一个调试信息 %d \n", __LINE__);
break;
case LED_OFF:
// 灯灭
gpiod_set_value(gpiono[0], 0);
break;
};
break;
case 1:
switch (cmd)
{
case LED_ON:
// 灯亮
gpiod_set_value(gpiono[1], 1);
break;
case LED_OFF:
// 灯灭
gpiod_set_value(gpiono[1], 0);
break;
};
break;
case 2:
switch (cmd)
{
case LED_ON:
// 灯亮
gpiod_set_value(gpiono[2], 1);
break;
case LED_OFF:
// 灯灭
gpiod_set_value(gpiono[2], 0);
break;
};
break;
}
return 0;
}
struct file_operations fops = {
.open = myled_open,
.unlocked_ioctl = myled_ioctl,
.release = myled_close,
};
static int __init myled_init(void)
{
// 获取设备树节点
dnode = of_find_node_by_path("/myled");
if (dnode == NULL)
{
printk("解析设备树结点失败\n");
return -ENXIO;
}
printk("解析设备树信息成功\n");
// 获取GPIO编号
gpiono[0] = gpiod_get_from_of_node(dnode, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);
gpiono[1] = gpiod_get_from_of_node(dnode, "led2-gpio", 0, GPIOD_OUT_LOW, NULL);
gpiono[2] = gpiod_get_from_of_node(dnode, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);
int i;
for (i = 0; i < 3; i++)
{
if (IS_ERR(gpiono[i]))
{
printk("申请GPIO对象失败\n");
return -ENXIO;
}
}
printk("申请gpio对象成功\n");
// 注册
major = register_chrdev(0, "myled", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major:%d\n", major);
// 自动创建设备节点
cls = class_create(THIS_MODULE, "myled");
if (IS_ERR(cls))
{
printk("向上提交目录信息失败\n");
return -PTR_ERR(cls);
}
printk("向上提交目录信息成功\n");
// 创建设备节点
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i + 1);
if (IS_ERR(dev))
{
printk("向上提交设备节点信息失败\n");
return -PTR_ERR(dev);
}
}
printk("向上提交设备节点信息成功\n");
return 0;
}
static void __exit myled_exit(void)
{
int i;
// 灭灯
for (i = 0; i < 3; i++)
{
gpiod_set_value(gpiono[i], 0);
}
// 释放GPIO编号
for (i = 0; i < 3; i++)
{
gpiod_put(gpiono[i]);
}
// 销毁设备节点信息
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
printk("设备节点信息已销毁\n");
// 销毁目录信息
class_destroy(cls);
printk("设备目录信息已销毁\n");
unregister_chrdev(major, "mychrdev");
printk("字符设备已注销\n");
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
3.编写应用程序测试
#include <stdio.h>
// open函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// ioctl函数头文件
#include <sys/ioctl.h>
//exit
#include <stdlib.h>
//close
#include <unistd.h>
// 宏定命令码
#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)
int main(int argc, const char *argv[])
{
// 1.选择操作哪盏灯
unsigned int ledx;
int fd;
while (1)
{
printf("选择点亮哪盏灯?1:LED1 2:LED2 3:LED3\n");
scanf("%d", &ledx);
switch (ledx)
{
case 1:
fd = open("/dev/myled1", O_RDWR);
break;
case 2:
fd = open("/dev/myled2", O_RDWR);
break;
case 3:
fd = open("/dev/myled3", O_RDWR);
break;
default:
printf("请输出合法数值\n");
continue;;
}
if (fd < 0)
{
printf("设备文件打开失败");
exit(-1);
}
printf("成功打开myled%d\n", ledx);
// 2.选择亮灭
printf("亮或灭?1:亮 0:灭\n");
scanf(" %d", &ledx);
switch (ledx)
{
case 1:
ioctl(fd, LED_ON);
break;
case 0:
ioctl(fd, LED_OFF);
break;
default:
printf("请输出合法数值\n");
break;
}
close(fd);
}
return 0;
}