07 - procfs

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

文章目录

  • 1. procfs 快速入门
  • 2. procfs 文件创建的回调机制
  • 3. 在 proc 目录下创建子目录
  • 4. 通过 proc 接口修改内核变量
  • 5. 通过 proc 接口访问数组
  • 6. 序列文件:seq_file 编程接口
  • 7. seq_file 工作机制分析
  • 8. 使用 seq_file 接口访问数组
  • 9. 使用 seq_file 接口访问链表
  • 10. 内核源码分析:proc/filesystems
  • 11. 内核源码分析:proc/interrupts
  • 12. 实战:通过 proc 接口调试 RTC 驱动
    • 12.1 之前的调试办法
    • 12.2 通过 proc 接口
  • 13. 实战:通过 proc 接口调试 RTC 寄存器

用户空间与内核空间的交互方式:文件 I/O(read、write)、ioctl、procfs、debugfs、sysfs、netlink
procfs 文件系统简介:基于内存的文件系统,进程的相关信息

  • proc 下面文件的生成机制、工作机制
  • 使用 proc 编程 API 接口去生成这些文件目录、删除目录、修改
  • 使用 proc 接口去调试内核模块或者驱动
    • 繁琐:驱动的寄存器:看手册、配置寄存器,编译,启动内核,调试–修改–编译–配置,耗时
    • 简化:通过 proc 直接修改寄存器

1. procfs 快速入门

注册:fs/proc/root.c
挂载:/etc/fstab,根文件系统挂载之后,接下来还会 mount 各种文件系统:procfs - /proc

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

2. procfs 文件创建的回调机制

  • 回调机制:与字符设备类似,我们在创建一个文件,需要自己实现它的回调函数:read、write
  • 重要结构体:
    file_operations:open、read、write、release、……
    proc_ops:proc_open、proc_read、proc_write、proc_release、……
  • 编程接口:
proc_create
remove_proc_entry
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>static char hello_buf[64] = "0";static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{/*buf: A pointer to the user space buffer where the data will be read into.count: The number of bytes to read from the kernel space buffer.ppos: A pointer to the file position. This is used to manage where in the kernel buffer the data is read from.hello_buf: A pointer to the buffer in kernel space that contains the data to be read.64: The total size of the kernel space buffer (from).*/return simple_read_from_buffer(buf, count, ppos, hello_buf, 64); 
}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;/* hello_buf is the destination buffer in kernel space.64 is the size of the buffer.ppos is the current position in the buffer.buf is the source buffer in user space.len is the number of bytes to write.*/ret = simple_write_to_buffer(hello_buf, 64, ppos, buf, len);return ret;
}static const struct proc_ops hello_ops = {.proc_read     = hello_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;if (proc_create(FILE_NAME, 0666, NULL, &hello_ops) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else printk("Create /proc/%s Success!\n", FILE_NAME);return ret;
}void __exit hello_procfs_exit(void)
{remove_proc_entry(FILE_NAME, NULL);printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_exit);MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := hello.oelse
EXTRA_CFLAGS += -DDEBUG 
KDIR := /home/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. 在 proc 目录下创建子目录

  • 如何创建子目录、在子目录下创建文件、删除子目录?
  • 编程接口:
proc_mkdir
proc_mkdir_mode
proc_create
proc_create_data
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>static char hello_buf[64] = "0";static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{return simple_read_from_buffer(buf, count, ppos, hello_buf, 64);
}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;ret = simple_write_to_buffer(hello_buf, 64, ppos, buf, len);return ret;
}static const struct proc_ops hello_ops = {.proc_read     = hello_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"
static struct proc_dir_entry *hello_root;
static struct proc_dir_entry *hello_subdir;
static struct proc_dir_entry *hello;int __init hello_procfs_init(void)
{hello_root = proc_mkdir("hello_root", NULL);if (NULL == hello_root) {printk("Create /proc/hello_root failed\n");return -1;}printk("Create /proc/hello_root success\n");hello_subdir = proc_mkdir_mode("hello_subdir", 666, hello_root);if (NULL == hello_subdir) {printk("Create /proc/hello_root/hello_subdir failed\n");goto error;}printk("Create /proc/hello_subdir success\n");hello = proc_create_data("hello", 666, hello_root, &hello_ops, NULL);if (NULL == hello) {printk("Create /proc/hello_root/hello failed\n");goto error;}printk("Create /proc/hello_root/hello success\n");return 0;
error:remove_proc_entry("hello_root", hello_root);return -1;
}void __exit hello_procfs_exit(void)
{remove_proc_entry("hello", hello_root);remove_proc_entry("hello_subdir", hello_root);remove_proc_entry("hello_root", NULL);printk("Remove /proc/hello subtree success\n");
}module_init(hello_procfs_init);
module_exit(hello_procfs_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. 通过 proc 接口修改内核变量

  • 重点:内核缓冲区和全局变量之间的转换

在这里插入图片描述

// cp指向字符串的开始,endp指向分析的字符串末尾的位置,base为要用的基数(进制数)
// base为0表示通过cp来自动判断基数,函数自动可识别的基数:‘0x’表示16进制,‘0’表示8进制,其它都认定为10进制。
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)  
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>static unsigned long hello_value;
static char hello_buf[64];static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{printk("hello_value = 0x%lx\n", hello_value);return simple_read_from_buffer(buf, count, ppos, &hello_value, 0);
}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;ret = simple_write_to_buffer(hello_buf, 64, ppos, buf, len);hello_value = simple_strtoul(hello_buf, NULL, 0); // base为0表示通过cp来自动判断基数return ret;
}static const struct proc_ops hello_ops = {.proc_read     = hello_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;if (proc_create(FILE_NAME, 0666, NULL, &hello_ops) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else {printk("Create /proc/%s Success!\n", FILE_NAME);}return ret;
}void __exit hello_procfs_exit(void)
{remove_proc_entry(FILE_NAME, NULL);printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_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. 通过 proc 接口访问数组

  • 内核中的全局变量:全局变量、结构体数组、链表、哈希树
  • 在read、write函数中通过 index 作为数组的索引去访问数组
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>struct hello_struct {unsigned int value;unsigned int id;
};static struct hello_struct hello_array[8];
static char hello_buf[64];
static int index = -1;static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{int ret = 0, i;int len = 4;for (i = 0; i < 8; i++) {if (hello_array[i].id) {printk("hello[%d].value = 0x%x\n", i, hello_array[i].value);    ret = copy_to_user(buf, &(hello_array[i].value), len);}}return ret;
}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;}index++;if (index == 8) {index = 0;}hello_array[index].id = index + 1;hello_array[index].value = simple_strtoul(hello_buf, NULL, 0);return len;
}static const struct proc_ops hello_ops = {.proc_read     = hello_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;if (proc_create(FILE_NAME, 0666, NULL, &hello_ops) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else {printk("Create /proc/%s Success!\n", FILE_NAME);}return ret;
}void __exit hello_procfs_exit(void)
{remove_proc_entry(FILE_NAME, NULL);printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_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

在这里插入图片描述

6. 序列文件:seq_file 编程接口

  • sequence file:序列文件
  • proc_ops:seq_open、seq_read、seq_write、seq_lseek、seq_release

在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>static unsigned int hello_value;
static char hello_buf[64];static int hello_show(struct seq_file *seq, void *v)
{unsigned int *p = seq->private;seq_printf(seq, "0x%x\n", *p);return 0;
}static int hello_open(struct inode *inode, struct file *file)
{//return single_open(file, hello_show, inode->i_private);return single_open(file, hello_show, PDE_DATA(inode));
}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;struct seq_file *seq = filp->private_data;unsigned int *p = seq->private;ret = simple_write_to_buffer(hello_buf, 64, ppos, buf, len);*p = simple_strtoul(hello_buf, NULL, 0) ;return ret;
}static const struct proc_ops hello_ops = {.proc_open     = hello_open,.proc_read     = seq_read,  // <--.proc_write    = hello_write,.proc_lseek    = seq_lseek,.proc_release  = seq_release,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;if (proc_create_data(FILE_NAME, 0666, NULL, &hello_ops, &hello_value) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else {printk("Create /proc/%s Success!\n", FILE_NAME);}return ret;
}void __exit hello_procfs_exit(void)
{remove_proc_entry(FILE_NAME, NULL);printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_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

在这里插入图片描述

7. seq_file 工作机制分析

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

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

8. 使用 seq_file 接口访问数组

  • 自定义 start、next、show、stop 函数来遍历数组元素,对数组元素依次进行访问
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>struct hello_struct {unsigned int value;unsigned int id;
};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_array[%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,.show = hello_seq_show,.stop = hello_seq_stop,
};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 proc_ops hello_ops = {.proc_open     = hello_open,.proc_read     = seq_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;if (proc_create(FILE_NAME, 0666, NULL, &hello_ops) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else {printk("Create /proc/%s Success!\n", FILE_NAME);}return ret;
}void __exit hello_procfs_exit(void)
{remove_proc_entry(FILE_NAME, NULL);printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_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

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

9. 使用 seq_file 接口访问链表

  • 编程接口:seq_list_start、seq_list_next

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

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>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, "value = 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 proc_ops hello_ops = {.proc_open     = hello_open,.proc_read     = seq_read,.proc_write    = hello_write,
};#define FILE_NAME "hello"int __init hello_procfs_init(void)
{int ret = 0;INIT_LIST_HEAD(&hello_list_head);if (proc_create(FILE_NAME, 0666, NULL, &hello_ops) == NULL) {ret = -ENOMEM;printk("Create /proc/%s failed\n", FILE_NAME);} else {printk("Create /proc/%s Success!\n", FILE_NAME);}return ret;
}void __exit hello_procfs_exit(void)
{struct hello_struct *data;remove_proc_entry(FILE_NAME, NULL);while (!list_empty(&hello_list_head)) {data = list_entry(hello_list_head.next, struct hello_struct, node);list_del(&data->node);kfree(data);}printk("Remove /proc/%s success\n", FILE_NAME);
}module_init(hello_procfs_init);
module_exit(hello_procfs_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

在这里插入图片描述

10. 内核源码分析:proc/filesystems

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

11. 内核源码分析:proc/interrupts

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

12. 实战:通过 proc 接口调试 RTC 驱动

  • 编写驱动,编译,加载运行,编写 app,通过文件 I/O 接口读写

在这里插入图片描述

  • 通过 proc 接口调试驱动

在这里插入图片描述

  • 重新编译内核,关掉自带的 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/proc_fs.h>
#include <linux/seq_file.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 0x10017000static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static char rtc_buf[64];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 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 dev_t devno;
static struct cdev *rtc_cdev;static int rtc_proc_show(struct seq_file *seq, void * v)
{cur_time = regs->RTCDR;rtc_time_translate();seq_printf(seq, "%u:%u:%u\n", tm.hour, tm.min, tm.sec);return 0;
}static int rtc_open(struct inode *inode, struct file *fp)
{single_open(fp, rtc_proc_show, PDE_DATA(inode));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;
}static ssize_t rtc_proc_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;ret = simple_write_to_buffer(rtc_buf, 64, ppos, buf, len);rtc_string_to_tm();rtc_tm_to_time();regs->RTCLR = cur_time;return len;
}static const struct file_operations rtc_fops = { // 字符设备操作.owner   = THIS_MODULE,.read    = rtc_read,.write   = rtc_write,.open    = rtc_open,.release = rtc_release,
};static const struct proc_ops rtc_proc_ops = { // proc操作.proc_open  = rtc_open,.proc_read  = seq_read,.proc_write = rtc_proc_write, 
};static struct proc_dir_entry *time;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 -1;} else {printk("Register char module: rtc success!\n");}time = proc_create_data("time", 0666, NULL, &rtc_proc_ops, NULL);if (time == NULL) {ret = -ENOMEM;printk("Create /proc/time failed\n");} else {printk("Create /proc/time success\n");}return 0;
}static void __exit rtc_exit(void)
{remove_proc_entry("time", NULL);printk("Remove /proc/time success\n");cdev_del(rtc_cdev);unregister_chrdev_region(devno, 1);printk("Goodbye char module: rtc!\n");
}module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>struct rtc_time{unsigned int year;unsigned int mon;unsigned int day;unsigned int hour;unsigned int min;unsigned int sec;
};int main(void)
{int fd;int len, ret;struct rtc_time tm;fd = open("/dev/rtc-demo", O_RDWR);if(fd == -1) {printf("cannot open file..\n");exit(1);}len = sizeof(struct rtc_time);if ((ret = read(fd, &tm, len)) < len) {printf("read error!\n");exit(1);}printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);tm.hour = 11;tm.min  = 11;tm.sec  = 11;ret = write(fd, &tm, len);if (ret < len) {printf("write error!\n");return -1;}ret = read(fd, &tm, len);if (ret < len) {printf("read error!\n");return -1;}printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);close(fd);return 0;
}
ifneq ($(KERNELRELEASE),)
obj-m := rtc.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

12.1 之前的调试办法

在这里插入图片描述

12.2 通过 proc 接口

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

13. 实战:通过 proc 接口调试 RTC 寄存器

  • 看手册,修改寄存器,编译,重启内核或者重新加载驱动,应用程序测试,SD 卡,插卡,拷贝,加载,拔卡 – 耗时
  • cat reg; echo 0x22334443 > reg – 提高效率
#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/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.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 0x10017000static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static char rtc_buf[64];
static unsigned long reg_RTCIMSC; // <------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 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 dev_t devno;
static struct cdev *rtc_cdev;static int rtc_proc_show(struct seq_file *seq, void * v)
{cur_time = regs->RTCDR;rtc_time_translate();seq_printf(seq, "%u:%u:%u\n", tm.hour, tm.min, tm.sec);return 0;
}static int rtc_open(struct inode *inode, struct file *fp)
{single_open(fp, rtc_proc_show, PDE_DATA(inode));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;
}static const struct file_operations rtc_fops = {.owner   = THIS_MODULE,.read    = rtc_read,.write   = rtc_write,.open    = rtc_open,.release = rtc_release,
};static ssize_t rtc_proc_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret;ret = simple_write_to_buffer(rtc_buf, 64, ppos, buf, len);rtc_string_to_tm();rtc_tm_to_time();regs->RTCLR = cur_time;return len;
}static const struct proc_ops rtc_proc_ops = {.proc_open  = rtc_open,.proc_read  = seq_read,.proc_write = rtc_proc_write, 
};static int reg_proc_show(struct seq_file *seq, void * v)
{seq_printf(seq, "reg_RTCIMSC: 0x%lx\n", reg_RTCIMSC);return 0;
}static int reg_proc_open(struct inode *inode, struct file *fp)
{single_open(fp, reg_proc_show, PDE_DATA(inode));return 0;
}static void set_rtc_alarm(void);
static ssize_t reg_proc_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{ssize_t ret; ret = simple_write_to_buffer(rtc_buf, 64, ppos, buf, len);reg_RTCIMSC = simple_strtoul(rtc_buf, NULL, 0);set_rtc_alarm();return len;    
}static const struct proc_ops reg_proc_ops = {.proc_open  = reg_proc_open,.proc_read  = seq_read,.proc_write = reg_proc_write, 
};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 + 5; // <-----regs->RTCICR = 1;regs->RTCIMSC = reg_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 proc_dir_entry *parent;
static struct proc_dir_entry *time;
static struct proc_dir_entry *reg;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 -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;}parent = proc_mkdir("rtc", NULL); // <------if (parent == NULL) {ret = -ENOMEM;printk("Create /proc/rtc failed\n");return ret;} else {printk("Create /proc/rtc success\n");}time = proc_create_data("time", 0666, parent, &rtc_proc_ops, NULL);if (time == NULL) {ret = -ENOMEM;printk("Create /proc/rtc/time failed\n");goto err;} else {printk("Create /proc/rtc/time success\n");}reg = proc_create_data("reg_RTCIMSC", 0666, parent, &reg_proc_ops, NULL);if (time == NULL) {ret = -ENOMEM;printk("Create /proc/rtc/reg_RTCIMSC failed\n");goto err;} else {printk("Create /proc/rtc/reg_RTCIMSC success\n");}set_rtc_alarm();return 0;
err:remove_proc_entry("rtc", parent);return ret;
}static void __exit rtc_exit(void)
{remove_proc_entry("time", parent);printk("Remove /proc/rtc/time success\n");remove_proc_entry("reg_RTCIMSC", parent);printk("Remove /proc/rtc/reg_RTCIMSC success\n");remove_proc_entry("rtc", NULL);printk("Remove /proc/rtc success\n");cdev_del(rtc_cdev);unregister_chrdev_region(devno, 1);printk("Goodbye char module: rtc!\n");
}module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := rtc.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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV绘图函数(1)绘制带箭头的直线函数arrowedLine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 绘制一个从第一个点指向第二个点的箭头线段。 cv::arrowedLine 函数在图像中绘制一个从 pt1 到 pt2 的箭头。另见 line 函数。 函数原型 void c…

基于单片机的无线空气质量检测系统设计

本设计以STC89C52单片机为核心&#xff0c;其中包含了温湿度检测模块、光照检测模块、PM2.5检测模块、报警电路、LCD显示屏显示电路、按键输入模块和无线传输模块来完成工作。首先&#xff0c;系统可以通过按键输入模块设置当前的时间和报警值&#xff1b;使用检测模块检测当前…

在Ubuntu 部署 Grafana且监控MySQL数据

一、安装 打开终端按顺序执行以下命令 1.添加 Grafana 的 APT 仓库&#xff1a; sudo apt-get install -y software-properties-common sudo add-apt-repository "deb https://packages.grafana.com/oss/deb stable main" 2.导入Grafana GPG key&#xff1a; wge…

吴光明铸就鱼跃辉煌,科技创新开辟医疗新篇章

在鱼跃集团的发展历程中&#xff0c;创始人吴光明为其树立了最鲜明的品牌标签——创新。吴光明始终坚信&#xff0c;“研发实力代表一个医疗器械企业的核心竞争力”。他很早就认识到&#xff0c;只有从用户需求出发进行创新&#xff0c;才能提升医疗产品的使用体验&#xff0c;…

软件设计原则之接口隔离原则

接口隔离原则&#xff08;Interface Segregation Principle, ISP&#xff09;是面向对象设计中的一个重要原则&#xff0c;它属于SOLID原则之一。这个原则强调客户端&#xff08;即接口的调用者&#xff09;不应该被迫依赖于它们不使用的方法。换句话说&#xff0c;一个类对另一…

SOA通信中间件介绍(一)

一、通信中间件 在软件定义汽车中&#xff0c;应用程序之间的跨进程或跨核通信是一个需要解决的问题。模块化架构为开发人员提供了便利&#xff0c;但也引入了对通信中间件的需求。 在没有使用通信中间件的情况下&#xff0c;开发人员需要自己定义数据的格式、发送方和接收方…

趣味呈现高效农业管理:智慧农场可视化

运用图扑自主研发的 HT 产品&#xff0c;全程零代码搭建 3D 轻量化 Low Poly 风格的智慧农场可视化&#xff0c;通过生动有趣的图形展示农场运作细节&#xff0c;使农业管理更直观易懂&#xff0c;提升管理效率和用户体验。

C++ 基础学习

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream>using namespace std;int main() {cout<<"请输入字符串:";string str;getline(cin,str);int num0;int alp0;int spa0;int other0;int …

网络安全面试经验分享:蘑菇街/网络安全

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 蘑菇街 介绍…

【STM32】MDK安装

1 MDK 历史背景 Keil公司是一家业界领先的微控制器&#xff08;MCU&#xff09;软件开发工具的独立供应商。Keil公司由两家私人公司联合运营&#xff0c;分别是德国慕尼黑的Keil Elektronik GmbH和美国德克萨斯的Keil Software Inc。Keil公司制造和销售种类广泛的开发工具&am…

自动化脚本到LabVIEW转换

工业自动化领域中的专用脚本语言转换为LabVIEW时需要注意的问题 语法差异&#xff1a; 脚本语言特点&#xff1a; 工业自动化脚本语言通常具有特定的语法和结构&#xff0c;例如条件判断、循环控制、硬件指令等。这些语言直接面向硬件操作&#xff0c;语法简洁&#xff0c;适用…

element-plus 新增一行合计。除了用summary-method还可以用append的插槽

:summary-method"getSummaries" <el-table:data"reformtableData"style"width: 100%"show-summary:summary-method"getSummaries"ref"reformtableRef" > <el-table-column label"序号" type"index…

MyBatis查询 ▎修改 ▎删除

前言: 在现代应用开发中&#xff0c;数据库操作是核心环节。MyBatis 作为一款灵活的持久层框架&#xff0c;提供了直接编写 SQL 语句的能力&#xff0c;避免了其他 ORM 框架可能带来的性能和功能限制。MyBatis 的查询、修改和删除操作是开发者必须掌握的基本技能。这些操作不仅…

go 使用 gitlab 搭建私有化模块系统

背景 本教程旨在教大家使用私有化部署的 gitlab 作为 go 的代码共享库&#xff0c;帮助团队分离代码模块&#xff0c;加强质量管控。go 官方在实现过程中就高度结合 VCS 系统&#xff0c; 可以仅通过配置相关的环境变量就实现私有库在 VCS 上的搭建。 代码分离样例 这里直接…

虚幻5|制作玩家血量,体力(还未编辑,只用于引用)

未编写&#xff0c;仅引用 优化后&#xff1a; 把增加生命&#xff0c;减少生命&#xff0c;也可以用在体力里&#xff0c;更改如下 限制浮点&#xff0c;如果血量或体力按10来扣&#xff0c;如果你的血量降低到5&#xff0c;那么就会以5的数值来扣&#xff0c;而不会扣成-5…

JVM极简教程

基础概念 1.1. Java 虚拟机 是运行 Java字节码的虚拟机 1.2. JVM跨平台原理 JVM在不同的系统&#xff08;Linux、Windows、MacOS&#xff09;上有不同的实现&#xff0c;目的是在使用相同的字节码&#xff0c;它们都会给出相同的结果 JVM跨平台本质&#xff1a;不同操作系统…

如何满足业主多元需求?开发物业APP,打造智能社区生活

随着智能科技的快速发展&#xff0c;物业管理也逐渐迈入数字化时代。物业app开发成为了提升社区管理效率、改善居民生活质量的重要途径&#xff0c;许多物业管理公司纷纷开发物业App&#xff0c;以提升管理效率、改善用户体验。一款出色的物业APP能够整合居民需求、提升企业服务…

Mybatis进阶——动态SQL

动态SQL 是Mybatis的强大特性之一&#xff0c;能够完成不同条件下的不同SQL拼接&#xff0c;可以参考官方文档&#xff1a;动态 SQL_MyBatis中文网 xml文件内容如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper PUBLI…

uniapp/uniapp x总结

uni-app组成和跨端原理 上图所诉 App的渲染引擎&#xff1a;同时提供了2套渲染引擎&#xff0c;.vue页面文件由webview渲染&#xff0c;原理与小程序相同&#xff1b;.nvue页面文件由原生渲染&#xff0c;原理与react native相同。开发者可以根据需要自主选择渲染引擎。 uniapp…

52.给定一个整数 n,实现一个算法返回 n 皇后不同的解决方案的数量

52. N-Queens II 题目 n皇后问题是指将n个皇后放置在一个nn的棋盘上,使得任意两个皇后不在同一行、同一列或同一对角线上。 给定一个整数 n,返回 n 皇后问题不同的解法数量。 示例: 输入: 4 输出: 2 解释: 4皇后问题有如下两个不同的解法: [ [“.Q…”, // 解法 1 “……