练习
基于platform实现
设备树
myplatform_homework{compatible = "hqyj,myplatform_homework"; //用于获取节点reg = <0x12345678 14>;interrupt-parent = <&gpiof>; //引用父节点interrupts = <9 0>; //这个节点引入的中断管脚led1-gpio = <&gpioe 10 0>;};
应用程序
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *agrv[])
{unsigned int number = 0;int fd = open("/dev/myplatform", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");return -1;}printf("打开设备文件成功\n");while (1){read(fd, &number, sizeof(number));printf("number = %d\n", number);}close(fd);return 0;
}
驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/interrupt.h>struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
unsigned int condition = 0;
unsigned int status = 0;
// 定义等待队列头
wait_queue_head_t wq_head;
unsigned int major = 0;
struct class *cls;
struct device *dev;// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{status = gpiod_get_value(gpiono);status = !status;gpiod_set_value(gpiono, status);condition = 1;wake_up_interruptible(&wq_head); // 唤醒休眠的进程return IRQ_HANDLED;
}// 封装操作方法
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{if (size > sizeof(status))size = sizeof(status);wait_event_interruptible(wq_head, condition);int ret = copy_to_user(ubuf, (void *)&status, size);if (ret){printk("copy_to_user failed\n");return ret;}condition = 0; // 表示下次的数据没有准备好return 0;
}
// 定义一个操作方法结构体变量并且初始化
struct file_operations fops = {.read = mycdev_read,
};// 1.封装probe函数和remove函数
int pdrv_probe(struct platform_device *pdev) // 在进入probe函数的时候节点已经被放在pdev->dev.of_node中了
{int ret;// 1.注册字符设备驱动major = register_chrdev(0, "myplatform", &fops);if (major < 0){printk("注册字符设备驱动失败\n");ret = major;goto ERR1;}printk("注册字符设备驱动成功major=%d\n", major);// 2.创建设备节点cls = class_create(THIS_MODULE, "myplatform");if (IS_ERR(cls)){printk("向上提交目录信息失败\n");ret = PTR_ERR(cls);goto ERR2;}printk("向上提交目录信息成功\n");dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myplatform");if (IS_ERR(dev)){printk("向上提交设备节点失败\n");ret = PTR_ERR(dev);goto ERR3;}printk("向上提交设备节点成功\n");// 3.初始化等待队列头init_waitqueue_head(&wq_head);gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);if (IS_ERR(gpiono)){printk("解析gpio信息失败\n");ret = PTR_ERR(gpiono);goto ERR4;}printk("解析gpio信息成功\n");irqno = platform_get_irq(pdev, 0); // 获取中断的信息if (irqno < 0){printk("获取中断类型资源失败\n");ret = irqno;goto ERR5;}printk("软中断号: %d\n", irqno);ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "myplatform", NULL);if (ret){printk("注册中断失败\n");goto ERR5;}printk("注册中断成功\n");return 0;
ERR5:gpiod_put(gpiono);
ERR4:device_destroy(cls, MKDEV(major, 0));
ERR3:class_destroy(cls);
ERR2:unregister_chrdev(major, "myplatform");
ERR1:return ret;
}
int pdrv_remove(struct platform_device *pdev)
{// 关灯gpiod_set_value(gpiono, 0);// 释放gpio信息gpiod_put(gpiono);unregister_chrdev(major, "myplatform");class_destroy(cls);device_destroy(cls, MKDEV(major, 0));free_irq(irqno, NULL);return 0;
}struct of_device_id oftable[] = {{.compatible = "hqyj,myplatform_homework"},{.compatible = "hqyj,myplatform_homework1"},{.compatible = "hqyj,myplatform_homework2"},{}, // 防止数组越界
};// 2.分配驱动信息对象并初始化
struct platform_driver pdrv = {.probe = pdrv_probe,.remove = pdrv_remove,.driver = {.name = "aaaaa", // 用于名字匹配.of_match_table = oftable, // 用于设备树匹配},
};// 一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");```