dev.c
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/interrupt.h> #include <linux/of_irq.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/fs.h> struct class *cls; struct resource *res; unsigned int irqno; struct cdev *cdev; unsigned int major = 0; unsigned int minor = 0; // 次设备号的起始值 dev_t devno; struct gpio_desc *gpiono; wait_queue_head_t wq_head; struct device *dev; char number = 0; unsigned int condition = 0; struct device_node *dnode; /*myplatform{ compatible = "hqyj,myplatform"; reg=<0X12345678 0X400>; interrupt-parent=<&gpiof>; interrupts=<9 0>; //9表示引用中断父节点时的索引信息 0表示默认设置 led1=<&gpioe 10 0>; };*/ // probe函数,匹配设备成功执行 int mycdev_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof) { int ret; if (sizeof(number) < size) size = sizeof(number); wait_event_interruptible(wq_head, condition); // 将进程切换为休眠 ret = copy_to_user(ubuf, (void *)&number, size); if (ret) { printk("copy_tO_user filed\n"); return -EIO; } printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); condition = 0; // 表示下一次硬件数据没有准备好 return 0; } int mycdev_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } struct file_operations fops = { .read = mycdev_read, .open = mycdev_open, .release = mycdev_close, }; irqreturn_t myirq_handler(int irqno, void *dev_id) { printk("key1 interrupt\n"); if (number == '0') { number = '1'; } else { number = '0'; } gpiod_set_value(gpiono, !gpiod_get_value(gpiono)); condition = 1; // 表示硬件数据就绪 wake_up_interruptible(&wq_head); // 唤醒休眠的进程 return IRQ_HANDLED; } 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; } printk("字符设备驱动对象申请成功\n"); // 2.字符设备驱动对象部分初始化 cdev_init cdev_init(cdev, &fops); // 3.申请设备号 register_chrdev_region/alloc_chrdev_region if (major > 0) // 静态申请设备号 { ret = register_chrdev_region(MKDEV(major, minor), 3, "myled"); if (ret) { printk("静态指定设备号失败\n"); goto out2; } } else // 动态申请设备号 { ret = alloc_chrdev_region(&devno, minor, 3, "myled"); if (ret) { printk("动态申请设备号失败\n"); goto out2; } major = MAJOR(devno); // 根据设备号得到主设备号 minor = MINOR(devno); // 根据设备号得到次设备号 } printk("申请设备号成功\n"); // 4.注册字符设备驱动对象 cdev_add() ret = cdev_add(cdev, MKDEV(major, minor), 3); if (ret) { printk("注册字符设备驱动对象失败\n"); goto out3; } printk("注册字符设备驱动对象成功\n"); // 5.向上提交目录 cls = class_create(THIS_MODULE, "myled"); if (IS_ERR(cls)) { printk("向上提交目录失败\n"); ret = -PTR_ERR(cls); goto out4; } printk("向上提交目录成功\n"); // 6.向上提交设备节点 for (i = 0; i < 3; i++) { dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i); if (IS_ERR(dev)) { printk("向上提交节点信息失败\n"); ret = -PTR_ERR(dev); goto out5; } } printk("向上提交设备节点信息成功\n"); printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); // 获取设备信息 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); ret = request_irq(irqno, myirq_handler, IRQF_TRIGGER_FALLING, "key", (void *)i); if (ret) { printk("注册驱动失败\n"); return ret; } // 获取gpio信息 // pdev->dev.of_node 设备树匹配之后会把设备树节点结构体首地址赋值给dev的of_node成员 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); } // 1.分配字符设备驱动对象空间 cdev_alloc return 0; out5: for (--i; i >= 0; i--) { // 销毁上面提交的设备信息 device_destroy(cls, MKDEV(major, i)); } 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) { int i = 0; printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); // 灭灯 gpiod_set_value(gpiono, 0); // s释放gpio信息 gpiod_put(gpiono); for (i = 0; i < 3; i++) { device_destroy(cls, MKDEV(major, i)); } free_irq(irqno, (void *)i); // 2.销毁目录 class_destroy class_destroy(cls); // 3.注销对象 cdev_del() cdev_del(cdev); // 4.释放设备号 unregister_chrdev_region() unregister_chrdev_region(MKDEV(major, minor), 3); // 5.释放对象空间 kfree() 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");
test.c
#include<stdlib.h> #include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #include<string.h> #include <sys/ioctl.h> #include <string.h> #include"head.h" int main(int argc, char const *argv[]) { int a,b; char num; int fd=open("/dev/myled0",O_RDWR); if(fd<0) { printf("打开设备文件失败\n"); exit(-1); } while(1) { //从终端读取 printf("正在读取number的值\n"); read(fd,&num,sizeof(num)); printf("number的值为%c\n",num); } close(fd); return 0; }