08 - debugfs

news2024/11/13 11:56:33

---- 整理自 王利涛老师 课程
实验环境:宅学部落 www.zhaixue.cc

文章目录

  • 0. 什么是 debugfs
  • 1. debugfs 配置编译和注册运行
  • 2. 第一个 debugfs 编程示例
  • 3. 通过 debugfs 导出整型数据
  • 4. 通过 debugfs 导出 16 进制数据
  • 5. 通过 debugfs 到处数组
  • 6. 通过 debugfs 导出内存数据块
  • 7. 通过 debugfs 导出自定义格式数据
  • 8. 在 debugfs 下使用 seq_file 接口
  • 9. 使用 seq_file 接口访问数组
  • 10. 使用 seq_file 接口访问链表
  • 11. 通过 debugfs 导出寄存器列表
  • 12. 通过 debugfs 修改 RTC 寄存器
  • 13. 通过 debugfs 导出 RTC 调试接口

0. 什么是 debugfs

  • 天生为调试内核而开发,一个基于内存的文件系统,它是基于 libfs,主要调试功能
  • 相比 procfs、sysfs 的优势
    • procfs:调试内核、修改寄存器,进程的信息导出到用户空间
      proc:需要实现它对应的底层的 read、write 接口,需要添加很多的调试代码
    • sysfs:设备模型导出到用户空间
    • debugfs:简化了导出的接口,一行代码,就可以将内核变量、数组、链表、内存中数据、寄存器导出导出到用户空间,方便调试

1. debugfs 配置编译和注册运行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 第一个 debugfs 编程示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>

static unsigned int hello_value;
static struct dentry *hello_root;

static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_u32("hello_reg", 0644, hello_root, &hello_value);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
    //debugfs_remove();
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述

3. 通过 debugfs 导出整型数据

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>

static unsigned int hello_value;
static struct dentry *hello_root;

static u8 u8_a;
static u16 u16_a;
static u64 u64_a;
static size_t size_t_a;
static unsigned long ulong_a;
static bool bool_a;


static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_u32("hello_reg", 0644, hello_root, &hello_value);
    debugfs_create_u8("u8_reg", 0644, hello_root, &u8_a);
    debugfs_create_u16("u16_reg", 0644, hello_root, &u16_a);
    debugfs_create_u64("u64_reg", 0644, hello_root, &u64_a);
    debugfs_create_size_t("size_t_reg", 0644, hello_root, &size_t_a);
    debugfs_create_ulong("ulong_reg", 0644, hello_root, &ulong_a);
    debugfs_create_bool("bool_reg", 0644, hello_root, &bool_a);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root)
        debugfs_remove_recursive(hello_root);
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 通过 debugfs 导出 16 进制数据

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>

static unsigned int hello_value;
static struct dentry *hello_root;

static u8 u8_a;
static u16 u16_a;
static u64 u64_a;

static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_x32("hello_reg", 0644, hello_root, &hello_value);
    debugfs_create_x8("u8_reg", 0644, hello_root, &u8_a);
    debugfs_create_x16("u16_reg", 0644, hello_root, &u16_a);
    debugfs_create_x64("u64_reg", 0644, hello_root, &u64_a);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

5. 通过 debugfs 到处数组

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>

static struct dentry *hello_root;
static u32 hello_array[8] = {1,2,3,4,5,6,7,8};

static struct debugfs_u32_array array_info = {
    .array = hello_array,
    .n_elements = 8,
};

static int __init hello_debugfs_init(void)
{
    int ret = 0;

    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_u32_array("hello_array", S_IWUGO, hello_root, &array_info);

    return ret;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

6. 通过 debugfs 导出内存数据块

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>

static struct dentry *hello_root;
static u32 hello_array[8] ={ 65, 66, 67, 68, 69, 70, 70, 71 };
static char *mem_block_p = NULL;

static struct debugfs_u32_array array_info = {
    .array = hello_array,
    .n_elements = 8,
};

static struct debugfs_blob_wrapper hello_blob = {
    .data = hello_array,
    .size = 8 * sizeof(u32),
};

static struct debugfs_blob_wrapper hello_mem;


static int __init hello_debugfs_init(void)
{
    int ret = 0;

    mem_block_p = kmalloc(32, GFP_ATOMIC);
    if (!mem_block_p) {
        pr_err("%s: kmalloc memory failed\n", __func__);
        return -ENOMEM;
    }
    memcpy(mem_block_p, "hello zhaixue.cc\n", 20);
    hello_mem.data = mem_block_p;
    hello_mem.size = 32;

    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_u32_array("hello_array", S_IWUGO, hello_root, &array_info);
    debugfs_create_blob("hello_blob", S_IWUGO, hello_root, &hello_blob);
    debugfs_create_blob("hello_mem", S_IWUGO, hello_root, &hello_mem);

    return ret;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }

    kfree(mem_block_p);
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

7. 通过 debugfs 导出自定义格式数据

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>

static struct dentry *hello_root;

static unsigned int hello_value;
static char hello_buf[100];


static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,
                            loff_t *ppos)
{
    int ret;

    if (*ppos >= 100)
        return 0;
    if (*ppos + count > 100)
        count = 100 - *ppos;

    ret = copy_to_user(buf, hello_buf, count);
    if (ret) {
        return -EFAULT;
    }
    *ppos += count;

    return count;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,
                            loff_t *ppos)
{
    int ret;
    
    if (*ppos > 100)
        return 0;
    if (*ppos + count > 100)
        count = 100 - *ppos;
    
    ret = copy_from_user(hello_buf, buf, count);
    if (ret)
        return -EFAULT;
    *ppos += count;

    return count;
}

static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .read = hello_read,
    .write = hello_write,
};

static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_x32("hello_reg", 0644, hello_root, &hello_value);
    debugfs_create_file("hello_buffer", S_IWUGO, hello_root, NULL, &hello_fops);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

8. 在 debugfs 下使用 seq_file 接口

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

static struct dentry *hello_root;

static char hello_buf[100];


static int hello_debugfs_show(struct seq_file *s, void *data)
{
    char* p = s->private;
    seq_printf(s, "%s", p);
    return 0;
}

static int hello_open(struct inode* inode, struct file* file)
{
    return single_open(file, hello_debugfs_show, inode->i_private);
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,
                            loff_t *ppos)
{
    int ret;
    
    if (*ppos > 100)
        return 0;
    if (*ppos + count > 100)
        count = 100 - *ppos;
    
    ret = copy_from_user(hello_buf, buf, count);
    if (ret)
        return -EFAULT;
    *ppos += count;

    return count;
}

static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,
    .read = seq_read,
    .write = hello_write,
    .llseek = seq_lseek,
    .release = single_release,
};

static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_file("hello_buffer", S_IWUGO, hello_root, hello_buf, &hello_fops);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

9. 使用 seq_file 接口访问数组

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

struct hello_struct {
    unsigned int value;
    unsigned int id;
};

static struct dentry *hello_root;
static struct hello_struct hello_array[8];
static char hello_buf[64];
static int index = 0;


static void *hello_seq_start(struct seq_file *s, loff_t *pos)
{
    printk("------start: *pos = %lld\n", *pos);
    if (*pos == 0) {
        return &hello_array[0];
    } else {
        *pos = 0;
        return NULL;
    }
}

static void *hello_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    struct hello_struct *p_node = NULL;
    (*pos)++;
    printk("------next: *pos = %lld\n", *pos);
    p_node = &hello_array[*pos];
    
    if (*pos == 8) {
        return NULL;
    }
    return p_node;
}

static void hello_seq_stop(struct seq_file *s, void *v)
{
    printk("stop\n");
}

static int hello_seq_show(struct seq_file *s, void *v)
{
    struct hello_struct *p = (struct hello_struct *)v;
    printk("------show: id = %d\n", p->id);
    if (p->id > 0) {
        seq_printf(s, "hello_arr[%d].value = 0x%x\n", p->id-1, \
                      hello_array[p->id-1].value);
    }

    return 0;
}

static struct seq_operations hello_seq_ops = {
    .start = hello_seq_start,
    .next  = hello_seq_next,
    .stop  = hello_seq_stop,
    .show  = hello_seq_show,
};

static int hello_open(struct inode* inode, struct file* file)
{
    return seq_open(file, &hello_seq_ops);
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len,
                            loff_t *ppos)
{
    int ret;
    
    if (len == 0 || len > 64) {
        ret = -EFAULT;
        return ret;
    }

    ret = copy_from_user(hello_buf, buf, len);
    if (ret) {
        return -EFAULT;
    }

    printk("hello_write: index = %d\n", index);
    hello_array[index].id = index + 1;
    hello_array[index].value = simple_strtoul(hello_buf, NULL, 0);
    index++;
    if (index == 8) {
        index = 0;
    }

    return len;
}

static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,
    .read = seq_read,
    .write = hello_write,
    .llseek = seq_lseek,
};

static int __init hello_debugfs_init(void)
{
    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_file("hello_buffer", S_IWUGO, hello_root, hello_array, &hello_fops);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述

10. 使用 seq_file 接口访问链表

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

static struct dentry *hello_root;

static char hello_buf[64];
static struct list_head hello_list_head;

struct hello_struct {
    unsigned int value;
    struct list_head node;
};


static void *hello_seq_start(struct seq_file *s, loff_t *pos)
{
   return seq_list_start(&hello_list_head, *pos);
}

static void *hello_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    return seq_list_next(v, &hello_list_head, pos);
}

static void hello_seq_stop(struct seq_file *s, void *v)
{
    // printk("stop\n");
}

static int hello_seq_show(struct seq_file *s, void *v)
{
    struct hello_struct *p_node = list_entry(v, struct hello_struct, node);
    seq_printf(s, "node = 0x%x\n", p_node->value);

    return 0;
}

static struct seq_operations hello_seq_ops = {
    .start = hello_seq_start,
    .next  = hello_seq_next,
    .stop  = hello_seq_stop,
    .show  = hello_seq_show,
};

static int hello_open(struct inode* inode, struct file* file)
{
    return seq_open(file, &hello_seq_ops);
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len,
                            loff_t *ppos)
{
    int ret;
    struct hello_struct *data;

    if (len == 0 || len > 64) {
        ret = -EFAULT;
        return ret;
    }

    ret = copy_from_user(hello_buf, buf, len);
    if (ret) {
        return -EFAULT;
    }

    data = kmalloc(sizeof(struct hello_struct), GFP_KERNEL);
    if (data != NULL) {
        data->value = simple_strtoul(hello_buf, NULL, 0);
        list_add(&data->node, &hello_list_head);
    }

    return len;
}

static struct file_operations hello_fops = {
    .open = hello_open,
    .read = seq_read,
    .write = hello_write,
    .llseek = seq_lseek,
};

static int __init hello_debugfs_init(void)
{
    int ret = 0;

    INIT_LIST_HEAD(&hello_list_head);

    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    debugfs_create_file("hello_list", S_IWUGO, hello_root, NULL, &hello_fops);

    return 0;
}

static void __exit hello_debugfs_exit(void)
{
    struct hello_struct *data;

    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }

    while (!list_empty(&hello_list_head)) {
        data = list_entry(hello_list_head.next, struct hello_struct, node);
        list_del(&data->node);
        kfree(data);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

11. 通过 debugfs 导出寄存器列表

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>

typedef volatile struct {
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time {
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static dev_t devno;
static struct cdev *rtc_cdev;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time  % 86400) / 3600;
    tm.min  = (cur_time  % 3600) / 60;
    tm.sec  = cur_time  % 60;
}

static void set_rtc_alarm(void)
{
    unsigned long tmp = 0;
    
    tmp = regs->RTCCR;
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    cur_time = regs->RTCDR;
    regs->RTCMR = cur_time + 15;

    regs->RTCICR = 0x1;
    regs->RTCIMSC = 0x1;

    tmp = regs->RTCCR;
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    cur_time = regs->RTCDR;
    rtc_time_translate();
    printk("\nalarm: beep~ beep~ \n");
    printk("\n    %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    regs->RTCICR = 1;
    set_rtc_alarm();
    return IRQ_HANDLED;
}

static struct file_operations rtc_fops;

static const struct debugfs_reg32 rtc_reg_array[] = {
    {
        .name   = "RTCDR",
        .offset = 0x0
    },
    {
        .name   = "RTCMR",
        .offset = 0x4
    },
    {
        .name   = "RTCLR",
        .offset = 0x8
    },
    {
        .name   = "RTCCR",
        .offset = 0xC
    },
    {
        .name   = "RTCIMSC",
        .offset = 0x10
    },
    {
        .name   = "RTCRIS",
        .offset = 0x14
    },
    {
        .name   = "RTCMIS",
        .offset = 0x18
    },
    {
        .name   = "RTCICR",
        .offset = 0x1C
    }
};

static struct debugfs_regset32 rtc_regset; // <------
static struct dentry *hello_root;

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc();
    cdev_init(rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_cdev, devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    set_rtc_alarm();

    hello_root = debugfs_create_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create rtc dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }

    rtc_regset.regs  = rtc_reg_array;
    rtc_regset.nregs = 8;
    rtc_regset.base  = (void *)regs;
    debugfs_create_regset32("reglist", 0644, hello_root, &rtc_regset); // <------

    return 0;
}

static void __exit rtc_exit(void)
{
    if (hello_root) {
        debugfs_remove_recursive(hello_root);
    }

    free_irq(39, NULL);
    cdev_del(rtc_cdev);
    unregister_chrdev_region(devno, 1);
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12. 通过 debugfs 修改 RTC 寄存器

  • 关闭内核自带的 rtc 驱动
  • 打开 debugfs 选项

在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>

typedef volatile struct {
    unsigned int  RTCDR;    /* +0x00: data register */
    unsigned int  RTCMR;    /* +0x04: match register */
    unsigned int  RTCLR;    /* +0x08: load register */
    unsigned int  RTCCR;    /* +0x0C: control register */
    unsigned int  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned int  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned int  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned int  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time {
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time  % 86400) / 3600;
    tm.min  = (cur_time  % 3600) / 60;
    tm.sec  = cur_time  % 60;
}

static void rtc_tm_to_time(void)
{
    cur_time = tm.hour * 3600 + tm.min * 60 + tm.sec;
}


static dev_t devno;
static struct cdev *rtc_cdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) {
        printk("rtc_write error!\n");
        return -1;
    }
    rtc_tm_to_time();
    regs->RTCLR = cur_time;

    return len;
}

/* standard file I/O system call interface */
static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static void set_rtc_alarm(void)
{
    unsigned int tmp = 0;
    
    tmp = regs->RTCCR;
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    cur_time = regs->RTCDR;
    regs->RTCMR = cur_time + 25; // <------

    regs->RTCICR = 1;
    regs->RTCIMSC = 1; // <------

    tmp = regs->RTCCR;
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    cur_time = regs->RTCDR;
    rtc_time_translate();
    printk("\nalarm: beep~ beep~ \n");
    printk("\n    %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    regs->RTCICR = 1;
    set_rtc_alarm();
    return IRQ_HANDLED;
}


static struct dentry *rtc_root;
static struct dentry *reg_dir;
static u32 *reg_p = NULL;

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc();
    cdev_init(rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_cdev, devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -ret;
    } else {
        printk("Register char module: rtc success!\n");
    }
    
    ret = request_irq(39, rtc_alarm_handler, 0, "rtc-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -ret;
    }

    rtc_root = debugfs_create_dir("rtc", NULL);
    if (IS_ERR(rtc_root)) {
        pr_err("%s: create rtc dir failed\n", __func__);
        return PTR_ERR(rtc_root);
    }
    reg_dir = debugfs_create_dir("reg_list", rtc_root);
    if (IS_ERR(reg_dir)) {
        pr_err("%s: create reg dir failed\n", __func__);
        return PTR_ERR(reg_dir);
    }

    reg_p = (u32 *)regs;
    debugfs_create_x32("RTCDR", 0644, reg_dir, reg_p);
    debugfs_create_x32("RTCMR", 0644, reg_dir, reg_p + 0x01);
    debugfs_create_x32("RTCLR", 0644, reg_dir, reg_p + 0x02);
    debugfs_create_x32("RTCCR", 0644, reg_dir, reg_p + 0x03);
    debugfs_create_x32("RTCIMSC", 0644, reg_dir, reg_p + 0x04);
    debugfs_create_x32("RTCRIS", 0644, reg_dir, reg_p + 0x05);
    debugfs_create_x32("RTCMIS", 0644, reg_dir, reg_p + 0x06);
    debugfs_create_x32("RTCICR", 0644, reg_dir, reg_p + 0x07);


    set_rtc_alarm();

    return 0;
}

static void __exit rtc_exit(void)
{
    if (rtc_root) {
        debugfs_remove_recursive(rtc_root);
    }

    free_irq(39, NULL);
    cdev_del(rtc_cdev);
    unregister_chrdev_region(devno, 1);
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

13. 通过 debugfs 导出 RTC 调试接口

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>

typedef volatile struct {
    unsigned int  RTCDR;    /* +0x00: data register */
    unsigned int  RTCMR;    /* +0x04: match register */
    unsigned int  RTCLR;    /* +0x08: load register */
    unsigned int  RTCCR;    /* +0x0C: control register */
    unsigned int  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned int  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned int  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned int  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time {
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

#define CUR_TIME_CMD   1      /* command for set/get current time */
#define ALARM_TIME_CMD 2      /* command for set/get alarm time */

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;   /* current time */
static unsigned long alarm_time = 0; /* alarm time  */
static struct rtc_time tm; /* global time */
static char rtc_buf[64]; /* buffer for time string input from user space */

static void rtc_time_to_tm(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static void rtc_tm_to_time(unsigned long *time)
{
    *time = tm.hour * 3600 + tm.min * 60 + tm.sec;
}

static void rtc_string_to_tm(void)
{
    tm.hour = (rtc_buf[0] - '0') * 10 + (rtc_buf[1] - '0');
    tm.min  = (rtc_buf[2] - '0') * 10 + (rtc_buf[3] - '0');
    tm.sec  = (rtc_buf[4] - '0') * 10 + (rtc_buf[5] - '0');
}

static void rtc_tm_to_string(void)
{
    rtc_buf[0] = tm.hour / 10 + '0';
    rtc_buf[1] = tm.hour % 10 + '0';
    rtc_buf[2] = tm.min / 10 + '0';
    rtc_buf[3] = tm.min % 10 + '0';
    rtc_buf[4] = tm.sec / 10 + '0';
    rtc_buf[5] = tm.sec % 10 + '0';
    rtc_buf[6] = '\n';
    rtc_buf[7] = '\0';
}


static dev_t devno;
static struct cdev *rtc_cdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_to_tm(cur_time);
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) {
        printk("rtc_write error!\n");
        return -1;
    }
    rtc_tm_to_time(&cur_time);
    regs->RTCLR = cur_time;

    return len;
}

/* standard file I/O system call */
static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static void set_rtc_alarm(void)
{
    unsigned int tmp = 0;
    
    tmp = regs->RTCCR;
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    cur_time = regs->RTCDR;
    regs->RTCMR = alarm_time;

    regs->RTCICR = 1;
    regs->RTCIMSC = 1; // <------

    tmp = regs->RTCCR;
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    cur_time = regs->RTCDR;
    rtc_time_to_tm(cur_time);
    printk("\nalarm: beep~ beep~ \n");
    printk("\n    %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    regs->RTCICR = 1;

    return IRQ_HANDLED;
}

static ssize_t rtc_debugfs_read(struct file *filp, char __user *buf,
                                size_t count, loff_t *ppos, int cmd)
{
    int ret;

    if (*ppos >= 64)
        return 0;
    if (*ppos + count > 64)
        count = 64 - *ppos;

    if (cmd == CUR_TIME_CMD) {
        cur_time = regs->RTCDR;
        rtc_time_to_tm(cur_time);
    }

    if (cmd == ALARM_TIME_CMD) {
        alarm_time = regs->RTCMR;
        rtc_time_to_tm(alarm_time);
    }

    rtc_tm_to_string();

    ret = copy_to_user(buf, rtc_buf, count);
    if (ret)
        return -EFAULT;
   *ppos += count;

    return count;
}

static ssize_t rtc_debugfs_write(struct file *filp, const char __user *buf, 
                                     size_t count, loff_t *ppos, int cmd)
{
    int ret;
    
    if (*ppos > 64)
        return 0;
    if (*ppos + count > 64)
        count = 64 - *ppos;
    
    ret = copy_from_user(rtc_buf, buf, count);
    if (ret)
        return -EFAULT;
    *ppos += count;

    rtc_string_to_tm();

    if (cmd == CUR_TIME_CMD) {
        rtc_tm_to_time(&cur_time);
        regs->RTCLR = cur_time;
    }

    if (cmd == ALARM_TIME_CMD) {
        rtc_tm_to_time(&alarm_time);
        set_rtc_alarm();
    }

    return count;
}


/* set current time interface */
static ssize_t rtc_cur_time_read(struct file *filp, char __user *buf, 
                                     size_t count, loff_t *ppos)
{
    return rtc_debugfs_read(filp, buf, count, ppos, CUR_TIME_CMD);
}

static ssize_t rtc_cur_time_write(struct file *filp, const char __user *buf, 
                                     size_t count, loff_t *ppos)
{
    return rtc_debugfs_write(filp, buf, count, ppos, CUR_TIME_CMD);
}

static const struct file_operations rtc_cur_time_fops = {
    .read    = rtc_cur_time_read,
    .write   = rtc_cur_time_write,
};


/* set alarm time interface */
static ssize_t rtc_alarm_time_read(struct file *filp, char __user *buf, 
                                     size_t count, loff_t *ppos)
{
    return rtc_debugfs_read(filp, buf, count, ppos, ALARM_TIME_CMD);
}

static ssize_t rtc_alarm_time_write(struct file *filp, const char __user *buf, 
                                     size_t count, loff_t *ppos)
{
    return rtc_debugfs_write(filp, buf, count, ppos, ALARM_TIME_CMD);
}

static const struct file_operations rtc_alarm_time_fops = {
    .read    = rtc_alarm_time_read,
    .write   = rtc_alarm_time_write,
};


static struct dentry *rtc_root;
static struct dentry *reg_dir;
static u32 *reg_p = NULL;

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc();
    cdev_init(rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_cdev, devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -ret;
    } else {
        printk("Register char module: rtc success!\n");
    }
    
    ret = request_irq(39, rtc_alarm_handler, 0, "rtc-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -ret;
    }

    rtc_root = debugfs_create_dir("rtc", NULL);
    if (IS_ERR(rtc_root)) {
        pr_err("%s: create rtc dir failed\n", __func__);
        return PTR_ERR(rtc_root);
    }
    reg_dir = debugfs_create_dir("reg_list", rtc_root);
    if (IS_ERR(reg_dir)) {
        pr_err("%s: create reg dir failed\n", __func__);
        return PTR_ERR(reg_dir);
    }

    reg_p = (u32 *)regs;
    debugfs_create_x32("RTCDR", 0644, reg_dir, reg_p);
    debugfs_create_x32("RTCMR", 0644, reg_dir, reg_p + 0x01);
    debugfs_create_x32("RTCLR", 0644, reg_dir, reg_p + 0x02);
    debugfs_create_x32("RTCCR", 0644, reg_dir, reg_p + 0x03);
    debugfs_create_x32("RTCIMSC", 0644, reg_dir, reg_p + 0x04);
    debugfs_create_x32("RTCRIS", 0644, reg_dir, reg_p + 0x05);
    debugfs_create_x32("RTCMIS", 0644, reg_dir, reg_p + 0x06);
    debugfs_create_x32("RTCICR", 0644, reg_dir, reg_p + 0x07);

    debugfs_create_file("cur_time", 0644, rtc_root, NULL, &rtc_cur_time_fops);
    debugfs_create_file("alarm_time", 0644, rtc_root, NULL, &rtc_alarm_time_fops);
    
    return 0;
}

static void __exit rtc_exit(void)
{
    if (rtc_root) {
        debugfs_remove_recursive(rtc_root);
    }

    free_irq(39, NULL);
    cdev_del(rtc_cdev);
    unregister_chrdev_region(devno, 1);
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2083527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SQL】换座位

目录 题目 分析 代码 题目 表: Seat ---------------------- | Column Name | Type | ---------------------- | id | int | | student | varchar | ---------------------- id 是该表的主键&#xff08;唯一值&#xff09;列。 该表的每一行都表示学…

ComfyUI SDXL Prompt Styler 简介

SDXL Prompt Styler 来自于 comfyui-art-venture 节点 style 已经更新 旧版本的 sai-line art 变更为 line art log_prompt 已经更新 旧版本的 false 变更为 Yes 或 No style_name 已经更新 旧版本的 true &#xff08;不再适用&#xff09;&#xff08;可以尝试对应style中…

【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch12 随机森林(Random Forest)

系列文章目录 监督学习&#xff1a;参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归&#xff08;SAheart.csv&#xff09; 【学习笔记】 陈强-机器学习-Python-…

dht11 + Sc32440驱动

一、DHT11概述 1、简介 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器&#xff0c;它应用专用的数字模块采集技术和温湿度传感技术&#xff0c;确保产品具有极高的可靠性和卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件&#xff…

【专题】2024年中国AI人工智能基础数据服务研究报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37516 随着人工智能技术的迅猛发展&#xff0c;AI 基础数据服务行业迎来了前所未有的发展机遇。报告合集显示&#xff0c;2023 年中国 AI 基础数据服务市场规模达到 45 亿元&#xff0c;且未来五年复合增长率有望达到 30.4%。多模态大…

如何在不同设备上检查IP 地址?

IP 地址&#xff08;Internet 协议地址&#xff09;是网络上设备的唯一标识符。了解如何查找 IP 地址对于解决网络问题、设置网络设备和维护网络安全非常重要。本文将详细介绍如何在不同设备上检查 IP 地址&#xff0c;包括 Windows 计算机、Mac 计算机、智能手机&#xff08;A…

【Material-UI】Rating组件:如何使用Basic Rating实现多种评分方式

文章目录 一、Rating 组件概述1. 组件介绍2. Basic rating 的核心功能 二、Basic rating 的详细使用方法1. 受控组件&#xff08;Controlled&#xff09;2. 只读模式&#xff08;Read-only&#xff09;3. 禁用状态&#xff08;Disabled&#xff09;4. 无评分状态&#xff08;No…

嵌入式笔试准备

文件组合 将传输文件切分成多个部分&#xff0c;按照原排列顺序&#xff0c;每部分文件编号为一个正整数。 class Solution { public:vector<vector<int>> fileCombination(int target) {vector<vector<int>> res;int sum 0;for(int i1; i<targe…

VS2017+QT不能正常添加资源文件

is not in a subdirectory of the resource file.You now have the option to copy this file to a valid location. . 该错误原因是项目路径英文导致的&#xff0c;换成全中文路径就没问题了 具体步骤&#xff1a; 双击qrc文件&#xff0c;先添加前缀&#xff0c;然后添加图片…

机器学习之监督学习(一)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 机器学习之监督学习&#xff08;一&#xff09; 1.监督学习定义2.监督学习分类2.1回归 regression2.2 分类 classification 3.线性回归 linear regression3.1 单特征线性回归…

Android 突破边界:解密google Partner机制获取Resource

在 Android 应用开发中&#xff0c;除了可以查找系统中的特定 APK 并获取其信息外&#xff0c;还可以通过 Partner 机制获取 Partner APK 的资源文件&#xff0c;同时这种机制也是一种跨进程的通信方式。本文将进一步探讨这些内容。 1.Partner apk注册特定的action广播 /** M…

Windows上编译GTest并执行测试用例

目录 0 背景1 环境2 生成GTest动态库2.1 下载最新GTest库2.2 编译GTest2.2.1 cmake gtest2.2.1.1 遇到问题&#xff1a;target参数不对2.2.1.2 遇到问题&#xff1a;xxx thread 编译报错 2.2.2 用VS2015编译依赖库 3 依赖库部署3.1 遇到问题 4 编写测试用例5 效果展示 0 背景 …

python中列表的复制亦有区别

python中 对列表的复制可以直接新变量名字等于原有列表名字&#xff0c;或者 创建新的列表从原有列表一个个复制进来。他们的区别在于下面 两个图中&#xff1a;

Codeforces 1304C - Air Conditioner(1500)

Air Conditioner 题面翻译 一个餐馆中有个空调&#xff0c;每分钟可以选择上调 1 1 1 个单位的温度或下调 1 1 1 个单位的温度&#xff0c;当然你也可以选择不变&#xff0c;初始的温度为 m m m 。 有 n n n 个食客&#xff0c;每个食客会在 t i t_i ti​ 时间点到达&am…

【maven】阿里云仓库配置

阿里云公共仓库的配置看起来有多种类型的仓库: 配置指南 我的maven是idea 自带的:D:\Program Files\JetBrains\IntelliJ IDEA 2022.3.1\plugins\maven\lib\maven3\</

突破编程 C++ 设计模式(组合模式)详尽攻略

在软件开发中&#xff0c;设计模式为程序员提供了解决特定问题的最佳实践。设计模式不仅提高了代码的可复用性和可维护性&#xff0c;还能帮助团队更好地进行协作。在这篇文章中&#xff0c;我们将深入探讨组合模式——一种结构型设计模式。 组合模式允许你将对象组合成树形结…

哪里能免费申请IP SSL证书

一、选择可信赖的证书颁发机构 首先&#xff0c;需要选择一个可信赖的证书颁发机构&#xff08;CA&#xff09;。知名的CA机构如JoySSL、Symantec、GlobalSign等提供IP SSL证书服务。这些机构能够提供符合国际标准的SSL证书&#xff0c;确保数据传输的安全性和服务器的身份验证…

Docker 安装 SqlServer

摘要&#xff1a;我们工作当中经常需要拉取多个数据库实例出来做集群&#xff0c;做测试也好&#xff0c;通过 Docker 拉取 SqlServer 镜像&#xff0c;再通过镜像运行多个容器&#xff0c;几分钟就可以创建多个实例&#xff0c;效率是相当的高。 1. docker 拉取镜像 注意&am…

C++与OpenCV联袂打造:智能视觉识别技术的实践与探索

C与OpenCV联袂打造&#xff1a;智能视觉识别技术的实践与探索 1. 环境设置与准备工作1.1 安装OpenCV和配置开发环境1.1.1 下载OpenCV1.1.2 安装OpenCVWindows系统Linux系统 1.1.3 配置OpenCV库 1.2 C编译器的选择与配置1.2.1 Windows系统1.2.2 Linux系统1.2.3 编译器配置 1.3 选…

浏览器中的开源SQL可视化工具:sqliteviz

sqliteviz&#xff1a; 在浏览器中&#xff0c;即刻开启数据可视化之旅。- 精选真开源&#xff0c;释放新价值。 概览 sqliteviz是一个专为数据可视化而设计的单页离线优先PWA&#xff0c;它利用了现代浏览器技术&#xff0c;让用户无需安装任何软件即可在本地浏览器中进行SQL…