驱动代码:
#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;// 打开LEDfd = 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;
}