Bläddra i källkod

优化cash驱动,并添加对应的测试程序

liu qidong [url ssh://qidong.liu@10.2.90.253:29418/] 1 månad sedan
förälder
incheckning
5d124d9585
3 ändrade filer med 316 tillägg och 82 borttagningar
  1. 1 0
      Makefile
  2. 103 82
      cash_drawers.c
  3. 212 0
      test_app/cash_app.c

+ 1 - 0
Makefile

@@ -17,6 +17,7 @@ all:
 	make CROSS_COMPILE=~/timesys/SDK64Bit-V6_02_00/toolchain/bin/x86_64-timesys-linux-gnu- -C $(KERNELDIR) M=$(PWD) modules
 	~/timesys/SDK64Bit-V6_02_00/toolchain/bin/x86_64-timesys-linux-gnu-gcc test_app/setss.c -DPROJECT=$(PROJECT_NAME) -o test_app/setss
 	~/timesys/SDK64Bit-V6_02_00/toolchain/bin/x86_64-timesys-linux-gnu-gcc test_app/test_beep.c -DPROJECT=$(PROJECT_NAME) -o test_app/beep
+	~/timesys/SDK64Bit-V6_02_00/toolchain/bin/x86_64-timesys-linux-gnu-gcc test_app/cash_app.c -DPROJECT=$(PROJECT_NAME) -o test_app/cash_app
 
 clean:
 	rm -rf *.o test_app/setss

+ 103 - 82
cash_drawers.c

@@ -16,7 +16,7 @@
 #define CLASS_NAME "cashd_class"
 #define MINOR_BASE 0
 #define MINOR_COUNT 2
-#define BUFFER_SIZE PAGE_SIZE
+#define BUFFER_SIZE 4
 
 #define GPIO0_CTL 0xFD6D0B50
 #define GPIO0_STATUS 0xFD6D0940
@@ -26,7 +26,6 @@
 
 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;
@@ -38,49 +37,76 @@ struct cashd_device {
     bool can_write;
     unsigned int ctl_status;
     unsigned int in_status;
+    unsigned int status_changed;
+    int last_status;
     struct delayed_work delay_work1;
+    struct delayed_work delay_work2;
+    void __iomem *gpio_ctl_reg_base;
+    void __iomem *gpio_status_reg_base;
+    int count;
 };
 
 static int cashd_major = 0;
 static struct class *cashd_class = NULL;
 static struct cashd_device *cashd_devices[MINOR_COUNT];
+static unsigned int minors[MINOR_COUNT] = {10, 11};
 
-static void delay_work_func(struct work_struct *work)
+static void gpio_ctl_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)
+
+    value = readl(dev->gpio_ctl_reg_base);
+    value &= 0xfffffffe;
+    writel(value, dev->gpio_ctl_reg_base);
+}
+
+static void gpio_status_delay_work_func(struct work_struct *work)
+{
+    unsigned int value = 0;
+    struct delayed_work *dwork = to_delayed_work(work);
+    struct cashd_device *dev = container_of(dwork, struct cashd_device, delay_work2);
+
+    value = readl(dev->gpio_status_reg_base);
+    value = value & 0x1;
+    if(value != dev->last_status)
     {
-        reg_base = ioremap(GPIO0_CTL, 0x1000);
+        dev->last_status = value;
+        dev->status_changed = 1;
+        wake_up_interruptible(&dev->read_wait);
     }
-    else if(dev->dev_minor == 1)
+    dev->count++;
+    if(dev->count >= 50)
     {
-        reg_base = ioremap(GPIO1_CTL, 0x1000);
+        dev->count = 0;
+        dev->status_changed = 1;
+        printk("%s %s %d\n", __FILE__, __func__, __LINE__);
+        wake_up_interruptible(&dev->read_wait);
     }
-    value = readl(reg_base);
-    value &= 0xfffffffe;
-    writel(value, reg_base);
+    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
+    schedule_delayed_work(&dev->delay_work2, msecs_to_jiffies(100));
 }
 
 static int cashd_open(struct inode *inode, struct file *filp)
 {
-    struct cashd_device *dev;
+    struct cashd_device *dev = NULL;
     unsigned int minor = iminor(inode);
-    
-    if (minor >= MINOR_COUNT) {
-        pr_err("cashd: Invalid minor number %d\n", minor);
-        return -ENODEV;
+    int i = 0;
+    for(i = 0; i < MINOR_COUNT; i++)
+    {
+        if(minor == minors[i])
+        {
+            dev = cashd_devices[i];
+            break;
+        }
     }
     
-    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);
     
@@ -99,45 +125,36 @@ static ssize_t cashd_read(struct file *filp, char __user *buf,
 {
     struct cashd_device *dev = filp->private_data;
     ssize_t bytes_read = 0;
-    size_t available;
-    int ret;
+    int ret = 0;
 
-    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);
-    }
+
+    dev->in_status = readl(dev->gpio_status_reg_base);
 
     if(dev->in_status & 0x1)
     {
-        copy_to_user(buf, "o", 1);
+        ret = copy_to_user(buf, "o", 1);
+        if(ret != 0)
+        {
+            return -EFAULT;
+        }
     }
     else
     {
-        copy_to_user(buf, "c", 1);
+        ret = copy_to_user(buf, "c", 1);
+        if(ret != 0)
+        {
+            return -EFAULT;
+        }
     }
 
+    dev->status_changed = 0;
+
     bytes_read = 1;
-    *f_pos += bytes_read;
     
     return bytes_read;
 }
@@ -146,11 +163,8 @@ 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;
@@ -160,20 +174,17 @@ static ssize_t cashd_write(struct file *filp, const char __user *buf,
         count = BUFFER_SIZE;
     }
     ret = copy_from_user(dev->buffer, buf, count);
+    if(ret != 0)
+    {
+        return -EFAULT;
+    }
     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 = readl(dev->gpio_ctl_reg_base);
         value |= 0x1;
-        writel(value, reg_base);
+        writel(value, dev->gpio_ctl_reg_base);
         schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(1000));
     }
     else
@@ -195,13 +206,9 @@ static unsigned int cashd_poll(struct file *filp, poll_table *wait)
     // poll_wait(filp, &dev->write_wait, wait);
     
     mutex_lock(&dev->lock);
-    
-    if (dev->data_size > 0)
+    if(dev->status_changed)
         mask |= POLLIN | POLLRDNORM;
     
-    // if (dev->data_size < dev->buffer_size)
-    //     mask |= POLLOUT | POLLWRNORM;
-    
     mutex_unlock(&dev->lock);
     
     return mask;
@@ -213,7 +220,7 @@ static const struct file_operations cashd_fops = {
     .release = cashd_release,
     .read = cashd_read,
     .write = cashd_write,
-    // .poll = cashd_poll,
+    .poll = cashd_poll,
 };
 
 static int __init cashd_init_device(struct cashd_device *dev, int minor)
@@ -227,7 +234,6 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
     }
     
     memset(dev->buffer, 0, BUFFER_SIZE);
-    dev->data_size = 0;
     dev->buffer_size = BUFFER_SIZE;
     dev->dev_major = cashd_major;
     dev->dev_minor = minor;
@@ -238,7 +244,8 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
     init_waitqueue_head(&dev->write_wait);
     
     mutex_init(&dev->lock);
-    INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
+    INIT_DELAYED_WORK(&dev->delay_work1, gpio_ctl_delay_work_func);
+    INIT_DELAYED_WORK(&dev->delay_work2, gpio_status_delay_work_func);
     
     cdev_init(&dev->cdev, &cashd_fops);
     dev->cdev.owner = THIS_MODULE;
@@ -250,33 +257,31 @@ static int __init cashd_init_device(struct cashd_device *dev, int minor)
         return ret;
     }
     
+    
     return 0;
 }
 
+static char *my_devnode(struct device *dev, umode_t *mode) {
+    if (mode) {
+        *mode = 0666;
+    }
+    return NULL;
+}
+
 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_major = 56;
+
     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;
     }
+	cashd_class->devnode = my_devnode;
     
     for (i = 0; i < MINOR_COUNT; i++) {
         cashd_devices[i] = kzalloc(sizeof(struct cashd_device), GFP_KERNEL);
@@ -286,7 +291,7 @@ int cashd_init(void)
             goto fail_devices;
         }
         
-        ret = cashd_init_device(cashd_devices[i], i);
+        ret = cashd_init_device(cashd_devices[i], minors[i]);
         if (ret) {
             pr_err("cashd: Failed to init device for minor %d\n", i);
             kfree(cashd_devices[i]);
@@ -295,7 +300,7 @@ int cashd_init(void)
         }
         
         device = device_create(cashd_class, NULL, 
-                               MKDEV(cashd_major, i), NULL,
+                               MKDEV(cashd_major, minors[i]), NULL,
                                "cashd%d", i);
         if (IS_ERR(device)) {
             ret = PTR_ERR(device);
@@ -305,17 +310,30 @@ int cashd_init(void)
             cashd_devices[i] = NULL;
             goto fail_devices;
         }
+
+        if(i == 0)
+        {
+            cashd_devices[i]->gpio_ctl_reg_base = ioremap(GPIO0_CTL, 4);
+            cashd_devices[i]->gpio_status_reg_base = ioremap(GPIO0_STATUS, 4);
+        }
+        else if(i == 1)
+        {
+            cashd_devices[i]->gpio_ctl_reg_base = ioremap(GPIO1_CTL, 4);
+            cashd_devices[i]->gpio_status_reg_base = ioremap(GPIO1_STATUS, 4);            
+        }
+        schedule_delayed_work(&cashd_devices[i]->delay_work2, msecs_to_jiffies(100));
         
         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));
+            device_destroy(cashd_class, MKDEV(cashd_major, minors[i]));
             cdev_del(&cashd_devices[i]->cdev);
             if (cashd_devices[i]->buffer)
                 kfree(cashd_devices[i]->buffer);
@@ -337,13 +355,16 @@ void cashd_exit(void)
     
     for (i = 0; i < MINOR_COUNT; i++) {
         if (cashd_devices[i]) {
-            device_destroy(cashd_class, MKDEV(cashd_major, i));
+            cancel_delayed_work_sync(&cashd_devices[i]->delay_work1);
+            cancel_delayed_work_sync(&cashd_devices[i]->delay_work2);
+            device_destroy(cashd_class, MKDEV(cashd_major, minors[i]));
             
             cdev_del(&cashd_devices[i]->cdev);
             
             if (cashd_devices[i]->buffer)
                 kfree(cashd_devices[i]->buffer);
-            
+            iounmap(cashd_devices[i]->gpio_ctl_reg_base);
+            iounmap(cashd_devices[i]->gpio_status_reg_base);
             kfree(cashd_devices[i]);
             
             pr_info("cashd: Removed /dev/cashd%d\n", i);

+ 212 - 0
test_app/cash_app.c

@@ -0,0 +1,212 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/time.h>
+
+#define BUFFER_SIZE 1024
+
+void print_usage(const char *prog_name)
+{
+    fprintf(stderr, "使用方法: %s <mode> <device>\n", prog_name);
+    fprintf(stderr, "  mode: 1 - 阻塞方式读取\n");
+    fprintf(stderr, "        0 - 非阻塞方式读取\n");
+    fprintf(stderr, "  device: 字符设备路径 (例如: /dev/cash0)\n");
+    fprintf(stderr, "示例:\n");
+    fprintf(stderr, "  %s 1 /dev/cash0   # 阻塞方式读取\n", prog_name);
+    fprintf(stderr, "  %s 0 /dev/cash0   # 非阻塞方式读取\n", prog_name);
+}
+
+int set_nonblocking(int fd)
+{
+    int flags = fcntl(fd, F_GETFL, 0);
+    if (flags == -1)
+    {
+        perror("fcntl F_GETFL");
+        return -1;
+    }
+
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+    {
+        perror("fcntl F_SETFL");
+        return -1;
+    }
+
+    return 0;
+}
+
+int set_blocking(int fd)
+{
+    int flags = fcntl(fd, F_GETFL, 0);
+    if (flags == -1)
+    {
+        perror("fcntl F_GETFL");
+        return -1;
+    }
+
+    if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1)
+    {
+        perror("fcntl F_SETFL");
+        return -1;
+    }
+
+    return 0;
+}
+
+// 使用 select 实现阻塞读取
+void blocking_read(int fd)
+{
+    fd_set read_fds;
+    char buffer[BUFFER_SIZE];
+    int ret;
+
+    printf("阻塞模式:等待数据...\n");
+
+    while (1)
+    {
+        FD_ZERO(&read_fds);
+        FD_SET(fd, &read_fds);
+
+        // NULL 表示无限等待,直到有数据可读
+        ret = select(fd + 1, &read_fds, NULL, NULL, NULL);
+
+        if (ret == -1)
+        {
+            if (errno == EINTR)
+            {
+                continue; // 被信号中断,继续等待
+            }
+            perror("select error");
+            break;
+        }
+
+        if (FD_ISSET(fd, &read_fds))
+        {
+            memset(buffer, 0, sizeof(buffer));
+            ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
+
+            if (n > 0)
+            {
+                buffer[n] = '\0';
+                printf("读到 %zd 字节: %s\n", n, buffer);
+
+                // 如果读到 "quit" 或 "exit" 则退出
+                if (strncmp(buffer, "quit", 4) == 0 ||
+                    strncmp(buffer, "exit", 4) == 0)
+                {
+                    printf("收到退出命令\n");
+                    break;
+                }
+            }
+            else if (n == 0)
+            {
+                printf("设备已关闭 (EOF)\n");
+                break;
+            }
+            else
+            {
+                if (errno != EAGAIN && errno != EWOULDBLOCK)
+                {
+                    perror("read error");
+                    break;
+                }
+            }
+        }
+    }
+}
+
+// 使用 select 实现非阻塞读取(带超时检测)
+void nonblocking_read(int fd)
+{
+    fd_set read_fds;
+    struct timeval timeout;
+    char buffer[BUFFER_SIZE];
+    int ret;
+
+    printf("非阻塞模式:\n");
+
+    while (1)
+    {
+        memset(buffer, 0, sizeof(buffer));
+        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
+
+        if (n > 0)
+        {
+            printf("读到 %zd 字节: %s\n", n, buffer);
+        }
+        sleep(1);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int mode;
+    const char *device_path;
+    int fd;
+    int open_flags;
+
+    // 检查参数
+    if (argc != 3)
+    {
+        print_usage(argv[0]);
+        return 1;
+    }
+
+    // 解析模式
+    mode = atoi(argv[1]);
+    if (mode != 0 && mode != 1)
+    {
+        fprintf(stderr, "错误: mode 必须是 0 或 1\n");
+        print_usage(argv[0]);
+        return 1;
+    }
+
+    device_path = argv[2];
+
+    // 打开设备
+    // 注意:select 对常规文件总是返回可读,但对字符设备通常能正确工作
+    // 为了更好的演示,我们以读写方式打开
+    open_flags = O_RDWR;
+
+    fd = open(device_path, open_flags);
+    if (fd < 0)
+    {
+        perror("打开设备失败");
+        fprintf(stderr, "设备: %s\n", device_path);
+        return 1;
+    }
+
+    printf("成功打开设备: %s\n", device_path);
+
+    // 根据模式设置阻塞/非阻塞
+    if (mode == 0)
+    {
+        // 非阻塞模式:设置 O_NONBLOCK 标志
+        if (set_nonblocking(fd) < 0)
+        {
+            close(fd);
+            return 1;
+        }
+        printf("设置为非阻塞模式\n");
+        nonblocking_read(fd);
+    }
+    else
+    {
+        // 阻塞模式:确保清除 O_NONBLOCK 标志
+        if (set_blocking(fd) < 0)
+        {
+            close(fd);
+            return 1;
+        }
+        printf("设置为阻塞模式\n");
+        blocking_read(fd);
+    }
+
+    close(fd);
+    printf("程序退出\n");
+
+    return 0;
+}