自己编写驱动,应用层程序,在应用层通过ioctl控制LED灯流水,当按键KEY1按下,让风扇转动(paltform框架的驱动形式)
新增设备树结点如下:
mycdev.h:
#ifndef __MYCDEV_H__
#define __MYCDEV_H__
//ioctl命令码
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)
#endif
pdrv.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include "mycdev.h"
struct cdev *cdev;
dev_t devno; //设备号
unsigned int minor = 0;
unsigned int major = 0;
struct class *cls;
struct device *dev;
struct gpio_desc *gpion1; //用于记录gpio编号
struct gpio_desc *gpion2;
struct gpio_desc *gpion3;
struct gpio_desc *gpio_fan;
struct gpio_desc *gpio_buzzer;
struct gpio_desc *gpio_motor;
int irqno[3]; // 用于记录获取到的软中断号
// 中断处理函数
irqreturn_t irq_handler(int irqno, void *arg)
{
switch((unsigned int)arg)
{
case 1:
gpiod_set_value(gpio_fan, !gpiod_get_value(gpio_fan)); //风扇状态反转
break;
case 2:
gpiod_set_value(gpio_buzzer, !gpiod_get_value(gpio_buzzer)); //蜂鸣器状态反转
break;
case 3:
gpiod_set_value(gpio_motor, !gpiod_get_value(gpio_motor)); //马达状态反转
break;
}
return IRQ_HANDLED;
}
int myled_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
// 判断应用层传递过来的命令码 cmd
// 判断操作哪盏灯进行点亮
switch (MINOR(file->f_inode->i_rdev)) // 判断次设备号
{
case 0: // 规定次设备号为0的设备文件只能控制LED1
if (cmd == LED_ON)
gpiod_set_value(gpion1, 1);
else if (cmd == LED_OFF)
gpiod_set_value(gpion1, 0);
break;
case 1: // 规定次设备号为1的设备文件只能控制LED2
if (cmd == LED_ON)
gpiod_set_value(gpion2, 1);
else if (cmd == LED_OFF)
gpiod_set_value(gpion2, 0);
break;
case 2: // 规定次设备号为2的设备文件只能控制LED3
if (cmd == LED_ON)
gpiod_set_value(gpion3, 1);
else if (cmd == LED_OFF)
gpiod_set_value(gpion3, 0);
break;
}
return 0;
}
int myled_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = myled_open,
.unlocked_ioctl = myled_ioctl,
.release = myled_close,
};
//probe函数
int pdrv_probe(struct platform_device *pdev)
{
int ret, i;
// 分配字符设备驱动对象
cdev = cdev_alloc();
if (NULL == cdev)
{
printk("分配对象空间失败\n");
ret = -ENOMEM;
goto ERR1;
}
printk("1.分配对象空间成功\n");
// 初始化驱动对象结构体
cdev_init(cdev, &fops);
// 动态申请设备号
ret = alloc_chrdev_region(&devno, minor, 3, "mycdev");
if (ret)
{
printk("动态申请设备号失败\n");
goto ERR2;
}
major = MAJOR(devno); // 获取主设备号
minor = MINOR(devno); // 获取次设备号
printk("2.动态申请设备号成功\n");
// 注册对象
ret = cdev_add(cdev, MKDEV(major, minor), 3);
if (ret)
{
printk("驱动对象注册进内核失败\n");
goto ERR3;
}
printk("3.驱动对象注册进内核成功\n");
// 向上提交目录
cls = class_create(THIS_MODULE, "mycdev");
if (IS_ERR(cls))
{
printk("向上提交目录失败\n");
goto ERR4;
}
printk("4.向上提交目录成功\n");
// 向上提交设备节点
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("向上提交节点信息失败\n");
goto ERR5;
}
}
printk("5.向上提交设备节点成功\n");
//获取gpio编号
gpion1 = gpiod_get_from_of_node(pdev->dev.of_node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpion1))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpion1);
}
gpion2 = gpiod_get_from_of_node(pdev->dev.of_node,"led2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpion2))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpion2);
}
gpion3 = gpiod_get_from_of_node(pdev->dev.of_node,"led3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpion3))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpion3);
}
gpio_fan = gpiod_get_from_of_node(pdev->dev.of_node,"fan",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpio_fan))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpio_fan);
}
gpio_buzzer = gpiod_get_from_of_node(pdev->dev.of_node,"buzzer",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpio_buzzer))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpio_buzzer);
}
gpio_motor = gpiod_get_from_of_node(pdev->dev.of_node,"motor",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(gpio_motor))
{
printk("获取gpio编号失败\n");
return PTR_ERR(gpio_motor);
}
printk("解析gpio编号成功\n");
// 根据设备树结点获取软中断号
for (i = 1; i <= 3; i++)
{
irqno[i-1] = irq_of_parse_and_map(pdev->dev.of_node, i-1);
if (!irqno[i-1])
{
printk("获取软中断号%d失败\n",i);
return -ENXIO;
}
printk("根据设备树结点获取软中断号成功\n");
// 注册要使用的中断
ret = request_irq(irqno[i-1], irq_handler, IRQF_TRIGGER_FALLING, "myirq", (void*)i);
if (ret)
{
printk("注册中断%d失败\n",i);
return ret;
}
printk("注册中断成功\n");
}
return 0;
ERR5:
// 将前面提交成功的设备信息销毁
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(major, i));
}
// 销毁目录
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), 3);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
//remove函数
int pdrv_remove(struct platform_device *pdev)
{
int i;
// 关闭风扇、蜂鸣器、马达
gpiod_set_value(gpio_fan, 0);
gpiod_set_value(gpio_buzzer, 0);
gpiod_set_value(gpio_motor, 0);
// 注销中断
for(i = 1; i <= 3; i++)
{
free_irq(irqno[i-1], (void*)i);
}
// 将LED灯全部熄灭
gpiod_set_value(gpion1, 0);
gpiod_set_value(gpion2, 0);
gpiod_set_value(gpion3, 0);
// 注销gpio编号
gpiod_put(gpion1);
gpiod_put(gpion2);
gpiod_put(gpion3);
gpiod_put(gpio_fan);
gpiod_put(gpio_buzzer);
gpiod_put(gpio_motor);
printk("注销gpio编号成功\n");
// 1.注销向上提交的字符设备信息
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
printk("1.注销向上提交的字符设备信息成功\n");
// 2.注销向上提交的目录信息
class_destroy(cls);
printk("2.注销向上提交的目录信息成功\n");
// 3.注销字符设备驱动对象
cdev_del(cdev);
printk("3.注销字符设备驱动对象成功\n");
// 4.释放申请到的设备号
unregister_chrdev_region(MKDEV(major, minor), 3);
printk("4.释放申请到的设备号成功\n");
// 5.释放申请到的字符设备驱动对象空间
kfree(cdev);
printk("5.释放申请到的字符设备驱动对象空间成功\n");
return 0;
}
//构建设备树匹配
struct of_device_id device_tree_table[] = {
{.compatible = "hqyj,platform"},
{},
};
//分配对象并初始化
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "aaaaa",
.of_match_table = device_tree_table,
},
};
//一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
text.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mycdev.h"
int main(int argc, const char *argv[])
{
char buf[128] = {0};
int fd[3] = {0};
char *dir[3] = {"/dev/mycdev0","/dev/mycdev1","/dev/mycdev2"};
for (int i = 0; i < 3; i++)
{
fd[i] = open(dir[i], O_RDWR);
if (-1 == fd[i])
{
perror("open is error\n");
return -1;
}
}
while (1)
{
for (int i = 0; i < 3; i++)
{
ioctl(fd[i], LED_ON);
sleep(1);
ioctl(fd[i], LED_OFF);
sleep(1);
}
}
for (int i = 0; i < 3; i++)
close(fd[i]);
return 0;
}