#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; struct delayed_work delay_work1; }; static int cashd_major = 0; static struct class *cashd_class = NULL; static struct cashd_device *cashd_devices[MINOR_COUNT]; static void delay_work_func(struct work_struct *work) { void __iomem *reg_base; unsigned int value = 0; struct delayed_work *dwork = to_delayed_work(work); struct cashd_device *dev = container_of(dwork, struct cashd_device, delay_work1); printk("%s %s %d\n", __FILE__, __func__, __LINE__); 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 &= 0xfffffffe; writel(value, reg_base); } 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; } 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') { printk("%s %s %d minor=%d\n", __FILE__, __func__, __LINE__, dev->dev_minor); 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); schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(1000)); } else { printk(KERN_INFO "cashd: Invalid input\n"); } return count; } 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); INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func); 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"); }