// cashd.c - 双节点字符设备驱动 #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "cashd" #define CLASS_NAME "cashd_class" #define MINOR_BASE 0 #define MINOR_COUNT 2 #define BUFFER_SIZE PAGE_SIZE #define GPIO0_CTL 0xFD6D0B50 #define GPIO0_STATUS 0xFD6D0940 #define GPIO1_CTL 0xFD6D0B60 #define GPIO1_STATUS 0xFD6D0950 // 每个设备节点的私有数据结构 struct cashd_device { unsigned char *buffer; size_t data_size; size_t buffer_size; wait_queue_head_t read_wait; wait_queue_head_t write_wait; struct mutex lock; struct cdev cdev; int dev_major; int dev_minor; bool can_read; bool can_write; unsigned int ctl_status; unsigned int in_status; }; static int cashd_major = 0; static struct class *cashd_class = NULL; static struct cashd_device *cashd_devices[MINOR_COUNT]; // 文件打开操作 static int cashd_open(struct inode *inode, struct file *filp) { struct cashd_device *dev; unsigned int minor = iminor(inode); if (minor >= MINOR_COUNT) { pr_err("cashd: Invalid minor number %d\n", minor); return -ENODEV; } dev = cashd_devices[minor]; if (!dev) { pr_err("cashd: Device not initialized for minor %d\n", minor); return -ENODEV; } // 将私有数据保存在file结构中 filp->private_data = dev; pr_info("cashd: Device /dev/cashd%d opened\n", minor); return 0; } // 文件释放操作 static int cashd_release(struct inode *inode, struct file *filp) { unsigned int minor = iminor(inode); pr_info("cashd: Device /dev/cashd%d closed\n", minor); return 0; } // 读操作 static ssize_t cashd_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct cashd_device *dev = filp->private_data; ssize_t bytes_read = 0; size_t available; int ret; void __iomem *reg_base; if(*f_pos > 0) return 0; if (!dev) return -EINVAL; if (!buf || count == 0) return -EINVAL; if(dev->dev_minor == 0) { reg_base = ioremap(GPIO0_STATUS, 0x1000); dev->in_status = readl(reg_base); printk(KERN_INFO "cashd: GPIO0 Status: 0x%x\n", dev->in_status); } else if(dev->dev_minor == 1) { reg_base = ioremap(GPIO1_STATUS, 0x1000); dev->in_status = readl(reg_base); printk(KERN_INFO "cashd: GPIO1 Status: 0x%x\n", dev->in_status); } if(dev->in_status & 0x1) { copy_to_user(buf, "o", 1); } else { copy_to_user(buf, "c", 1); } bytes_read = 1; *f_pos += bytes_read; return bytes_read; } // 写操作 static ssize_t cashd_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct cashd_device *dev = filp->private_data; ssize_t bytes_written = 0; size_t space; int ret; unsigned int value = 0; void __iomem *reg_base; if (!dev) return -EINVAL; if(count > BUFFER_SIZE) { count = BUFFER_SIZE; } ret = copy_from_user(dev->buffer, buf, count); if(dev->buffer[0] == 'o' || dev->buffer[0] == 'O') { if(dev->dev_minor == 0) { reg_base = ioremap(GPIO0_CTL, 0x1000); } else if(dev->dev_minor == 1) { reg_base = ioremap(GPIO1_CTL, 0x1000); } value = readl(reg_base); value |= 0x1; writel(value, reg_base); } else { printk(KERN_INFO "cashd: Invalid input\n"); } return count; } // poll操作 - 支持select/poll/epoll static unsigned int cashd_poll(struct file *filp, poll_table *wait) { struct cashd_device *dev = filp->private_data; unsigned int mask = 0; if (!dev) return POLLERR; // 添加等待队列 poll_wait(filp, &dev->read_wait, wait); // poll_wait(filp, &dev->write_wait, wait); mutex_lock(&dev->lock); // 可读条件:缓冲区有数据 if (dev->data_size > 0) mask |= POLLIN | POLLRDNORM; // // 可写条件:缓冲区有空间 // if (dev->data_size < dev->buffer_size) // mask |= POLLOUT | POLLWRNORM; mutex_unlock(&dev->lock); return mask; } // 文件操作结构体 static const struct file_operations cashd_fops = { .owner = THIS_MODULE, .open = cashd_open, .release = cashd_release, .read = cashd_read, .write = cashd_write, // .poll = cashd_poll, }; // 初始化设备 static int __init cashd_init_device(struct cashd_device *dev, int minor) { int ret; // 分配缓冲区 dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); if (!dev->buffer) { pr_err("cashd: Failed to allocate buffer for minor %d\n", minor); return -ENOMEM; } // 初始化字段 memset(dev->buffer, 0, BUFFER_SIZE); dev->data_size = 0; dev->buffer_size = BUFFER_SIZE; dev->dev_major = cashd_major; dev->dev_minor = minor; dev->can_read = false; dev->can_write = true; // 初始化等待队列 init_waitqueue_head(&dev->read_wait); init_waitqueue_head(&dev->write_wait); // 初始化互斥锁 mutex_init(&dev->lock); // 初始化cdev cdev_init(&dev->cdev, &cashd_fops); dev->cdev.owner = THIS_MODULE; // 添加字符设备到系统 ret = cdev_add(&dev->cdev, MKDEV(cashd_major, minor), 1); if (ret) { pr_err("cashd: Failed to add cdev for minor %d\n", minor); kfree(dev->buffer); return ret; } return 0; } // 模块初始化 int cashd_init(void) { int ret; int i; dev_t dev_num; struct device *device; pr_info("cashd: Initializing driver\n"); // 动态分配主设备号 ret = alloc_chrdev_region(&dev_num, MINOR_BASE, MINOR_COUNT, DEVICE_NAME); if (ret < 0) { pr_err("cashd: Failed to allocate device numbers\n"); return ret; } cashd_major = MAJOR(dev_num); pr_info("cashd: Allocated major number %d\n", cashd_major); // 创建设备类 cashd_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(cashd_class)) { ret = PTR_ERR(cashd_class); pr_err("cashd: Failed to create class\n"); goto fail_class; } // 为每个次设备号创建设备 for (i = 0; i < MINOR_COUNT; i++) { // 分配设备结构体 cashd_devices[i] = kzalloc(sizeof(struct cashd_device), GFP_KERNEL); if (!cashd_devices[i]) { pr_err("cashd: Failed to allocate device for minor %d\n", i); ret = -ENOMEM; goto fail_devices; } // 初始化设备 ret = cashd_init_device(cashd_devices[i], i); if (ret) { pr_err("cashd: Failed to init device for minor %d\n", i); kfree(cashd_devices[i]); cashd_devices[i] = NULL; goto fail_devices; } // 创建设备节点 device = device_create(cashd_class, NULL, MKDEV(cashd_major, i), NULL, "cashd%d", i); if (IS_ERR(device)) { ret = PTR_ERR(device); pr_err("cashd: Failed to create device for minor %d\n", i); cdev_del(&cashd_devices[i]->cdev); kfree(cashd_devices[i]); cashd_devices[i] = NULL; goto fail_devices; } pr_info("cashd: Created /dev/cashd%d\n", i); } pr_info("cashd: Driver initialized successfully\n"); return 0; fail_devices: // 清理已初始化的设备 for (i = 0; i < MINOR_COUNT; i++) { if (cashd_devices[i]) { device_destroy(cashd_class, MKDEV(cashd_major, i)); cdev_del(&cashd_devices[i]->cdev); if (cashd_devices[i]->buffer) kfree(cashd_devices[i]->buffer); kfree(cashd_devices[i]); } } class_destroy(cashd_class); fail_class: unregister_chrdev_region(MKDEV(cashd_major, MINOR_BASE), MINOR_COUNT); return ret; } // 模块清理 void cashd_exit(void) { int i; pr_info("cashd: Cleaning up driver\n"); // 销毁设备和释放资源 for (i = 0; i < MINOR_COUNT; i++) { if (cashd_devices[i]) { // 销毁设备节点 device_destroy(cashd_class, MKDEV(cashd_major, i)); // 删除字符设备 cdev_del(&cashd_devices[i]->cdev); // 释放缓冲区 if (cashd_devices[i]->buffer) kfree(cashd_devices[i]->buffer); // 释放设备结构体 kfree(cashd_devices[i]); pr_info("cashd: Removed /dev/cashd%d\n", i); } } // 销毁设备类 if (cashd_class) class_destroy(cashd_class); // 注销设备号 unregister_chrdev_region(MKDEV(cashd_major, MINOR_BASE), MINOR_COUNT); pr_info("cashd: Driver cleaned up\n"); }