07 - procfs

news2024/9/22 9:53:00

---- 整理自 王利涛老师 课程
实验环境:宅学部落 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.o

else
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 0x10017000

static 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 0x10017000

static 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.coloradmin.cn/o/2081903.html

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

相关文章

Hbase离线迁移

假设是hbase集群&#xff0c;那么数据存储在hdfs上。 1.关闭2个hbase 2.使用distcp 将hdfs上的hbase数据迁移到另一个【相同路径】的hdfs上。 不知道目录的话&#xff0c;可以find / -name hbase-site.xml找一下。 hadoop distcp -Dmapreduce.job.hdfs-servers.token-renew…

XtQuant接口概述,想用miniQMT做量化哪家券商支持?

XtQuant.XtData 行情模块 xtdata是xtquant库中提供行情相关数据的模块&#xff0c;本模块旨在提供精简直接的数据满足量化交易者的数据需求&#xff0c;作为python库的形式可以被灵活添加到各种策略脚本中。 主要提供行情数据&#xff08;历史和实时的K线和分笔&#xff09;、…

无人机喊话器详解!!!

无人机喊话器&#xff0c;也被称为无人机扬声器&#xff0c;是一种安装在无人机上&#xff0c;用于通过空中向地面人员传递声音的设备。 一、功能特点 远程传递声音&#xff1a;无人机喊话器能够在较远的距离内清晰地传递声音&#xff0c;有效广播范围通常可达数百米甚至更远…

Linux下单网卡配置多个路由ip方法

Linux下配置网卡ip别名何谓ip别名 用windows的话说&#xff0c;就是为一个网卡配置多个ip。 什么场合增加ip别名能派上用场&#xff1f; 布网需要、多ip访问测试、特定软件对多ip的需要 下面通过几个例子简单介绍一下如何使用ifconfig命令给网卡配置ip别名。 一、首先为服务器…

会员通知短信怎么利用NodeJS发送短信

会员通知群发短信以其即时高效、高打开率、个性化定制、成本效益高、跨平台兼容以及法律合规等优势&#xff0c;在现代会员管理和营销策略中占据了重要地位。对于希望提升会员沟通效率、增强用户粘性和促进业务增长的企业而言&#xff0c;合理利用群发短信工具无疑是一个明智的…

left join 使用 sum()

一&#xff0c;表结构 表一&#xff0c;test_group 分组表 表二&#xff0c;test_user 用户表 test_group 和 test_user 是一对多的关系 二&#xff0c;sql 统计一班的总得分和所有用户的总年龄 SELECT SUM(a.score),SUM(b.age),a.groupname from test_group a LEFT JOIN…

Keepalived高可用配置服务器集群

前言 上章完成了通过nginx对服务器配置了负载均衡,保证了一个服务器宕机另一个服务器顶上,但是有一个问题出现了,如果nginx宕机了怎么办,这章会对这个问题进行讲解,配置集群来保证nginx宕机下一个顶上来,服务器一直能提供服务当一台服务器宕机的时候把从服务器切换为主,提供虚…

【机器人学】7-3.六自由度机器人自干涉检测-圆柱体的旋转变换【附MATLAB代码】

前言 上一章确定了机械臂等效的圆柱体的上下圆心坐标&#xff0c;这篇文章将解决算法三个核心中的第二个核心&#xff1a; 一 根据机械臂的几何数据以及DH参数&#xff0c;确定机械臂等效的圆柱体的上下圆心坐标。 二 将一个圆柱体旋转到与坐标Z轴对齐&#xff0c;另一个圆柱…

华为机考笔试没有结果?

点击上方"互联网求职达人"&#xff0c;选择"设为置顶or星标" 第一时间获取最实用的求职以及备考信息 华为机考笔试范围是什么&#xff1f;​https://mp.weixin.qq.com/s?__bizMzU2NjE0NjMxNg&mid2247483962&idx1&sn69b0b152dd8e7ebe9ddb356b…

livekit 环境搭建

目录 livekit介绍 搭建开发环境 goland编译 运行server 启动server 通过lk命令加入房间 通过web打开本地摄像头 通过goland调试livekit livekit介绍 livekit是一个开源的webrtc服务器&#xff0c;性能十分强大&#xff0c;房间管理、redis、信令业务、流媒体sfu都支…

PMP备考经验 | 如何做到一次考过3A?

一&#xff0c;直播课的学习 面对大量场景阅读题&#xff0c;且刷题也基本没原题的PMP考试&#xff0c;从报名的时候就知道临考刷题大法肯定是行不通的&#xff0c;也想把这个证好好学下去真的成为项目管理专家。 于是第一个周末的直播课两天跟随听课&#xff0c;认真做笔记&am…

数据结构——链表学习

数据结构初步了解 链表是数据结构的一部分&#xff0c;所以我想先理清数据结构的要点&#xff1a; 它们说&#xff1a;程序算法数据结构&#xff0c;在计算机中&#xff0c;把所有可以输入到计算机中能被计算机进行处理的符号的总称叫做数据。 数据结构包括两部分&#xff0…

美畅物联丨物联网平台的标准化之路:物模型设计的创新及应用

随着物联网&#xff08;IoT&#xff09;技术以前所未有的迅猛之势蓬勃发展&#xff0c;海量的物联网终端与应用纷纷接入&#xff0c;这不可避免地引发了数据与应用层面的异构化难题&#xff0c;进而形成了复杂且多变的碎片化问题。物联网感知数据因其具备多源异构的显著特性&am…

基于vue框架的病房管理系统设计与实现uilp1(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;患者,医护人员,患者病历,医院病房,住院信息,科室,病房预约,住院费用 开题报告内容 基于Vue框架的病房管理系统设计与实现开题报告 一、引言 随着医疗信息化建设的不断深入&#xff0c;病房管理作为医院日常运营的核心环节之一&#x…

VMware Workstation虚拟机 + 许可证密钥

VMware Workstation虚拟机 许可证密钥 VMware Workstation是什么&#xff1f;VMware简介VMware 安装VMware系统要求VMware 版本下载地址许可证序列码看这里 &#xff01;&#xff01;&#xff01;&#xff01;&#xff01; VMware Workstation是什么&#xff1f; VMware简介 …

揭开容器的面纱:容器技术全景概述

随着云计算的快速发展&#xff0c;容器技术已经成为IT行业的重要组成部分。Docker作为一种领先的容器化技术&#xff0c;为应用程序的开发、部署和运行带来了革命性的变化。本篇文章将详细介绍容器技术的概念、发展历程及其在现代计算中的应用。通过对Docker的深入了解&#xf…

友思特方案 | 基于三维点云实现PCB装配螺丝视觉检测

导读 三维点云是完成精密化 PCB 检测的最新视觉技术。友思特 Saccde Vision 视觉扫描系统&#xff0c;采用先进的三维成像技术和算法输出直观点云图&#xff0c;进一步确保了PCB生产的可靠性与稳定性能。 在电子产品的生产过程中&#xff0c;PCB&#xff08;Printed Circuit B…

streamlit+wordcloud使用pyinstaller打包遇到的一些坑

说明 相比常规的python程序打包&#xff0c;streamlit应用打包需要额外加一层壳&#xff0c;常规app.py应用运行直接使用 python app.py就可以运行程序了&#xff0c;但streamlit应用是需要通过streamlit命令来运行 streamlit app.py所以使用常规的pyinstaller app.py打包是…

阿里云服务器自带多少DDoS防护?小编为你详细揭秘!

大家好呀&#xff01;今天小编来聊聊一个很多朋友都关心的问题&#xff1a;阿里云服务器自带多少DDoS防护&#xff1f;随着网络攻击手段的日益猖獗&#xff0c;DDoS攻击对企业业务的威胁越来越大&#xff0c;选择一个安全可靠的云服务器至关重要。阿里云作为国内领先的云计算服…

宝宝自闭症的表现与理解

自闭症&#xff0c;又称孤独症&#xff0c;是一种复杂的神经心理障碍&#xff0c;它影响患儿的脑部发展&#xff0c;并在社交沟通、兴趣和行为上表现出明显的异常。自闭症通常在幼儿期就有所体现&#xff0c;但很多时候&#xff0c;家长在宝宝一岁前后才会开始注意到这些异常表…