一、什么是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");