驱动代码:
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
struct cdev* cdev;
unsigned int major = 500;
unsigned int minor = 0;
dev_t devno;
struct class* cls;
struct device* dev;
struct resource* res;
unsigned int irqno;
struct gpio_desc* gpiono;
unsigned int number;
// 定义等待队列头
wait_queue_head_t wq_head;
unsigned int condition = 0;
ssize_t mycdev_read(struct file* file, char* ubuf, size_t size, loff_t* lof)
{
int ret;
// 将进程切换到休眠状态
wait_event_interruptible(wq_head, condition);
if (size > sizeof(number)) {
size = sizeof(number);
}
ret = copy_to_user(ubuf, &number, size);
if (ret) {
printk("copy_to_user filed\n");
return -EIO;
}
condition = 0; // 下次硬件的数据没有准备好
return size;
}
irqreturn_t myirq_led_handler(int irqno, void* dev_id)
{
gpiod_set_value(gpiono, !gpiod_get_value(gpiono));
number = gpiod_get_value(gpiono);
condition = 1; // 数据准备就绪
// 唤醒休眠的进程
wake_up_interruptible(&wq_head);
return IRQ_HANDLED;
}
struct file_operations fops = {
.read = mycdev_read,
};
int pdrv_probe(struct platform_device* pdev)
{
int ret, i;
// 初始化等待队列头
init_waitqueue_head(&wq_head);
// 分配字符设备驱动对象空间
cdev = cdev_alloc();
if (cdev == NULL) {
printk("申请字符设备驱动空间失败\n");
ret = -EFAULT;
goto out1;
}
// 字符设备驱动对象部分初始化
cdev_init(cdev, &fops);
// 静态申请设备号
if (major > 0) {
ret = register_chrdev_region(MKDEV(major, minor), 3, "mykey");
if (ret) {
printk("静态指定设备号失败\n");
goto out2;
}
} else { // 动态申请设备号
ret = alloc_chrdev_region(&devno, minor, 3, "mykey");
if (ret) {
printk("动态指定设备号失败\n");
goto out2;
}
major = MAJOR(devno);
minor = MINOR(devno);
}
// 注册字符设备驱动对象
ret = cdev_add(cdev, MKDEV(major, minor), 3);
if (ret) {
printk("注册字符设备驱动对象失败\n");
goto out3;
}
printk("注册字符设备驱动对象成功\n");
// 向上提交目录
cls = class_create(THIS_MODULE, "mykey");
if (IS_ERR(cls)) {
printk("向上提交目录失败\n");
ret = -PTR_ERR(cls);
goto out4;
}
printk("向上提交目录成功\n");
// 向上提交设备节点
for (i = 0; i < 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mykey%d", i);
if (IS_ERR(dev)) {
printk("向上提交节点信息失败\n");
ret = -PTR_ERR(dev);
goto out5;
}
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// printk("向上提交设备节点信息成功\n");
// 获取设备信息
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk("获取资源失败\n");
return -ENXIO;
}
printk("获取资源信息成功 %x\n", res->start);
irqno = platform_get_irq(pdev, 0);
if (irqno < 0) {
printk("获取中断资源失败\n");
return irqno;
}
printk("中断类型资源为%d\n", irqno);
// 获取gpio信息
gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(gpiono)) {
printk("解析GPIO信息失败\n");
return -PTR_ERR(gpiono);
}
// 注册中断
ret = request_irq(irqno, myirq_led_handler, IRQF_TRIGGER_FALLING, "key1", NULL);
if (ret) {
printk("注册失败\n");
return ret;
}
return 0;
out5:
// 销毁提交的设备信息
device_destroy(cls, MKDEV(major, minor));
class_destroy(cls);
out4:
cdev_del(cdev);
out3:
unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
kfree(cdev);
out1:
return ret;
}
// remove 设备和驱动分离时执行
int pdrv_remove(struct platform_device* pdev)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 灭灯
gpiod_set_value(gpiono, 0);
// 释放gpio信息
free_irq(irqno, NULL);
gpiod_put(gpiono);
// 销毁设备信息
int i;
for (i = 0; i < 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
// 销毁目录
class_destroy(cls);
// 注销对象
cdev_del(cdev);
// 释放设备号
unregister_chrdev_region(MKDEV(major, minor), 3);
// 释放对象空间
kfree(cdev);
return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {
{
.compatible = "hqyj,myplatform",
},
{
.compatible = "hqyj,myplatform1",
},
{},
};
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "aaaaa",
.of_match_table = oftable,
},
};
// 一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
测试代码:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const* argv[])
{
int fd;
unsigned int number;
// 打开LED
fd = open("/dev/mykey0", O_RDWR);
if (fd < 0) {
printf("打开设备文件失败\n");
exit(-1);
}
while (1) {
read(fd, &number, sizeof(number));
printf("number=:%d\n", number);
}
close(fd);
return 0;
}