Linux内核编程(08):debugfs文件系统

一、什么是debugfs

简介:天生为调试内核而生,一个基于内存的文件系统,基于libfs开发的,提供的调试功能强大;相对于procfs、sysfs的优势:

​ procfs:使用procfs调试内核、修改寄存器,对于他的底层read、write接口添加了大量的调试代码。

​ sysfs:设备模型,导出到用户空间。

​ debugfs:简化了导处的接口,一行代码就可以将内核变量、数组、链表、内存中的数据、寄存器等信息进行导出。挂载在/sys/kernel/debug上。

二、debugfs配置编译和注册运行

​ linux-5.10:vexpress_defconfig

​ make vexpress_defconfig;NFS v4.x; make uImage; make all(用于生成Module.symvers文件,该文件在编译独立模块时会用到,有些正常编译时就会产生该文件,如果没有的话,我们再使用make all进行编译);

​ 内核中的很多模块,都用到了debugfs的API接口,CONFIG_DEBUG_FS,如果没有配置则对应的为空函数。

// /fs/debugfs/inode.c
early_param("debugfs", debugfs_kernel);
static int __init debugfs_init(void)
{
    int retval;

    if (!(debugfs_allow & DEBUGFS_ALLOW_MOUNT))
        return -EPERM;

    retval = sysfs_create_mount_point(kernel_kobj, "debug");
    if (retval)
        return retval;

    retval = register_filesystem(&debug_fs_type);
    if (retval)
        sysfs_remove_mount_point(kernel_kobj, "debug");
    else
        debugfs_registered = true;

    return retval;
}

三、第一个debugfs编程示例

​ 使用debugfs提供的API接口在debugfs目录下生成自己的文件或者目录。相关的API定义在include/linux/debugfs.h目录下。

#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
    hello_root = debugfs_crate_dir("hello", NULL);
    if (IS_ERR(hello_root)) {
        pr_err("%s: create debugfs dir failed.\n", __func__);
        return PTR_ERR(hello_root);
    }
     // 创建变量hello_reg
    debugfs_create_u32("hello_reg", 0644, hello_root, &hello_value);
	
    return 0;
}

static void __exit hello_exit(void)
{
    debugfs_remove_recursive(hello_root);
}

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

四、通过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 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_error("%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);
}

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);

五、通过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_error("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }
    
    // 带有x的函数在显示时,以16进制进行显示.
    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);
}

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);

六、通过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_error("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }
    
    // 通过debugfs_create_u32_array函数导出数组,读起来比较方便;写的时候相对不太行.
    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);

七、通过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 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_error("%s: create debugfs dir failed\n", __func__);
        return PTR_ERR(hello_root);
    }
    
    // 通过debugfs_create_u32_array函数导出数组,读起来比较方便;写的时候相对不太行.
    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);
    }
}

module_init(hello_debugfs_init);
module_exit(hello_debugfs_exit);

八、通过debugfs导出自定义格式数据

​ 自己构建适合我们格式的读写file_operation接口,来完成实现数据的读写。

#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 size_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 void 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)
{
    int ret = 0;
    
    mem_block_p = kmalloc(32, GFP_ATOMIC);
	if (!mem_block_p) {
        pr_err("%s: kmalloc memory failed\n", __func__);
        return -ENOMEM;
    }
	
    debugfs_create_x32("hello_reg", 0644, hello_root, &hello_value);
    debugfs_create_file("hello_buffer", S_IWUGO, hello_root, NULL, &hello_fops);
   
    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);

九、在debugfs使用seq_file接口

​ debugfs + seq_file接口配合使用

​ seq_read, seq_opertions: start/next/show/stop, show函数自己实现

​ seq_read, seq_opertions: 自己实现seq_operation的接口.

#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 *filp)
{
    return single_open(file, hello_debugfs_show, inode->i_private);
}

static size_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;
}

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("");
        return PTR_ERR(hello_root);
    }

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

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

module_init(hello_debugfs_init);
module_exit(hello_exit);

十、使用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) {
    //    *pos = 0;
        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 const struct file_operations hello_ops = {
    .owner    = THIS_MODULE,
    .open     = hello_open,
    .read     = seq_read,
    .write    = hello_write,
    .llseek    = seq_lseek,
};



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_file("hello_buffer", S_IWUGO, hello_root,  , 
                        &hello_ops);

    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);

十一、使用seq_file接口遍历链表

通过实现自定义的seq_operations接口,以及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 const struct file_operations hello_ops = {
    .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_ops);

    return ret;
}

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");
MODULE_AUTHOR("wit@zhaixue.cc");

十二、使用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函数最后一个参数为寄存器相关的结构体
    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");
MODULE_AUTHOR("wit@zhaixue.cc");

十三、使用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,即导出一个地址,可读可写.
    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");
MODULE_AUTHOR("wit@zhaixue.cc");

十四、通过debugfs导出调试接口

通过debugfs导出调试接口之后,用户调试程序就可以通过/sys/kernel/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

#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");
MODULE_AUTHOR("wit@zhaixue.cc");
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核探秘:深入解析文件系统和设备驱动的架构与设计》是一本非常有价值的书籍。它深入探索了Linux操作系统内核文件系统和设备驱动的架构和设计。 这本书首先介绍了Linux内核的基本概念和组成部分。它详细描述了Linux文件系统的设计原理和实现方式。文件系统是操作系统用于管理和组织文件的重要组成部分。该书详细介绍了Linux内核中常见的文件系统类型,如Ext4、Btrfs和F2FS,并深入探讨了文件系统的数据结构、缓存和访问控制等关键方面。 另外,该书还详细解析了Linux内核中的设备驱动程序。设备驱动程序是操作系统与硬件之间的桥梁。这本书介绍了设备驱动程序的基本原理和工作方式,包括设备驱动模型、设备节点和设备文件系统等。同时,书中还讨论了设备间通信和驱动程序的编写方法,并提供了实际案例进行说明。 这本书的特点是理论结合实践。书中提供了大量的示例代码和实际案例,让读者可以更好地理解和应用所学知识。此外,书中还提供了一些常见问题和解决方案,帮助读者更好地解决实际问题。 总之,《Linux内核探秘:深入解析文件系统和设备驱动的架构与设计》是一本对于想要深入了解Linux内核文件系统和设备驱动设计的读者非常有价值的书籍。无论是对于专业人士还是对于Linux爱好者来说,它都是一本不容错过的好书。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值