Quellcode durchsuchen

添加cash drawer驱动模块

liu qidong [url ssh://qidong.liu@10.2.90.253:29418/] vor 1 Monat
Ursprung
Commit
d67fa27a44
4 geänderte Dateien mit 381 neuen und 1 gelöschten Zeilen
  1. 1 1
      Makefile
  2. 370 0
      cash_drawers.c
  3. 7 0
      cash_drawers.h
  4. 3 0
      main.c

+ 1 - 1
Makefile

@@ -3,7 +3,7 @@ CROSS_COMPILE=arm-poky-linux-gnueabi-
 #也可以同时编译多个模块  obj-m += export_symbol.o export_symbol1.o export_symbol2.o
 obj-m := coral.o
 
-coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o
+coral-objs := main.o led.o light_ring.o ssegment.o ec_version.o buzzer.o fan.o writeprotect.o myname.o cash_drawers.o
 
 KERNELDIR := /lib/modules/$(shell uname -r)/build
 ccflags-y += -I./include

+ 370 - 0
cash_drawers.c

@@ -0,0 +1,370 @@
+// cashd.c - 双节点字符设备驱动
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/io.h>
+
+#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");
+}

+ 7 - 0
cash_drawers.h

@@ -0,0 +1,7 @@
+#ifndef __CASH_DRAWERS_H__
+#define __CASH_DRAWERS_H__
+
+void cashd_init(void);
+void cashd_exit(void);
+
+#endif

+ 3 - 0
main.c

@@ -24,6 +24,7 @@
 #include "fan.h"
 #include "writeprotect.h"
 #include "myname.h"
+#include "cash_drawers.h"
 
 struct kobject *vfiec_kobj = NULL;
 
@@ -37,6 +38,7 @@ static int __init all_driver_init(void)
         ret = -ENOMEM;
     }
 
+    cashd_init();
     myname_init();
     writeprotect_init();
     fan_init();
@@ -52,6 +54,7 @@ static int __init all_driver_init(void)
 
 static void __exit all_driver_exit(void)
 {
+    cashd_exit();
     myname_exit();
     writeprotect_exit();
     fan_exit();