linux driver和 /proc 文件的交互

        linux系统中的proc文件系统是一个虚拟文件系统,proc文件系统支持在应用层中通过echo,cat命令读写内核中driver的变量值。此方法可以用来调试驱动,也可以用在项目实际的需求当中。

        例如:echo 192.168.3.1/24 > /proc/net/cardX    # 把IP地址写到driver中,然后driver再写到网卡中。

        下面例子是在driver中创建proc文件,并且在应用层中对proc文件的读写操作。

smart_nic.c

static struct proc_dir_entry *proc_dir = NULL;
struct proc_dir_entry *proc_file;
DEFINE_SPINLOCK(proc_lock);

/* 最终在应用中cat时被调用 */
static int agile_proc_read(struct seq_file *m, void *v)
{
	u32 bin_addr = 0;
	u32 bin_netmask = 0;
	char netmask[20];
	char user_str[20];
	void __iomem *addr_ip = NULL;
	void __iomem *addr_netmask = NULL;
	
    //get private data 
	struct agile_pci_device *ap_dev  = (struct agile_pci_device *)m->private;
	
	if (!ap_dev) {
		pr_err("ap_dev is null\n");
		return -EFAULT;
	}
	spin_lock(&proc_lock);

	addr_ip= get_ip_addr(ap_dev);
	addr_netmask = get_netmask_addr(ap_dev);

	if(addr_ip)
		bin_addr = ioread32(addr_ip);
	if(addr_netmask)
		bin_netmask = ioread32(addr_netmask);

	snprintf(netmask, sizeof(netmask), "%d", bin_netmask);
	sprintf (user_str, "%s/%s", inet_ntoa(bin_addr), netmask);  /* eg: user_str 192.168.1.1/24 */
	seq_printf(m, "%s\n", user_str);

	spin_unlock(&proc_lock);
	return 0;
}

static int agile_proc_open(struct inode *inode, struct file *file)
{
	/* malloc seq_operations 并初始化; 
     * 参3:传递private data,然后show和write中访问private data
     */
	return single_open(file, agile_proc_read, PDE_DATA(inode));
}

//proc_write被调用流程:
static ssize_t agile_proc_write(struct file *fp,const char __user *u_buffer, size_t count, loff_t *data)
{
	char buf[20] = {0};
    size_t buf_size = min(count, sizeof(buf) - 1);
	struct agile_pci_device *ap_dev = ((struct seq_file *)fp->private_data)->private;

    spin_lock(&proc_lock);

	if (copy_from_user(buf, u_buffer, buf_size)) {
		spin_unlock(&proc_lock);
		return -EFAULT;
	}
	buf[buf_size] = '\0';

    /* 驱动中对buf数据做必要的处理 ... ... */
    spin_unlock(&proc_lock);
	return buf_size;
}

#if LINUX_VERSION_CODE <= KERNEL_VERSION(5,6,0)
struct file_operations proc_fops =
{
	.open       = agile_proc_open,		/* cat called */
	.read       = seq_read,
	.write      = agile_proc_write,		/* echo called */
	.llseek     = seq_lseek,
	.release    = single_release,
};
#else
static const struct proc_ops proc_fops = {
	.proc_open	= agile_proc_open,
	.proc_read	= seq_read,		
	.proc_write	= agile_proc_write,
	.proc_lseek	= seq_lseek,
	.proc_release	= single_release,
};
#endif

/*
proc_ops 结构中函数跟踪:
seq_read()
	struct seq_file *m = iocb->ki_filp->private_data;
	err = m->op->show(m, p);	//op : const struct seq_operations *op;
	这里的show()就是在
	agile_proc_open
		single_open(file, agile_proc_read,,,)  中指定的show函数 agile_proc_read

single_open()			//proc_ops结构中。被 proc_ops 中的 open 调用
	seq_open(file, op);
*/	

//创建 proc文件
static bool agile_proc_init(struct agile_pci_device *ap_dev)
{
	char file_name[50] = {0};

    spin_lock(&proc_lock);
	sprintf(file_name, "%s%d", PROC_IP_FILE, ap_dev->proc_index);

	/*create /proc/net/agile */
    proc_dir = proc_mkdir(PROC_DIR, init_net.proc_net);	
    if(!proc_dir) {
		pr_err("create /proc/net/agile err\n");
		spin_unlock(&proc_lock);
		return false;
	}

	/*create /proc/net/agile/cardIPX, X = 0,1,...; 参5:private data */
	proc_file = proc_create_data(file_name, 0644, proc_dir, &proc_fops, ap_dev);
	if (!proc_file) {
		pr_err("create /proc/net/agile/catdIP err\n");
		remove_proc_entry(PROC_DIR, NULL);
		spin_unlock(&proc_lock);
		return false;
	}

    spin_unlock(&proc_lock);
	return true;	
}

// 删除 /proc文件
static void agile_proc_exit(struct agile_pci_device *ap_dev)
{
	char file_name[50];
	
	int index = proc_index;
	sprintf(file_name, "%s%d", PROC_IP_FILE, index);

    if (proc_file) {
		remove_proc_entry(file_name, proc_dir);
		proc_file = NULL;
	}

	if (proc_dir) {
		remove_proc_entry(PROC_DIR, init_net.proc_net);
		proc_dir = NULL;
	}
    spin_unlock(&proc_lock);
}

//在probe函数中调用 agile_proc_init(); remove函数中调用 agile_proc_exit() 即可。

----------------------------------------------------------------------------
上面函数被调用的流程为:
// fs\proc\inode.c
static const struct file_operations proc_reg_file_ops = {
	.read		= proc_reg_read,
	.write		= proc_reg_write,
	.open		= proc_reg_open,
	.release	= proc_reg_release,
};

proc_reg_write()
	pde_write()		// fs\proc\inode.c
		write = pde->proc_ops->proc_write;
				return write(file, buf, count, ppos);

proc_reg_read()
	pde_read()		// fs\proc\inode.c
	read = pde->proc_ops->proc_read;
			return read(file, buf, count, ppos);

proc_reg_open()
	open = pde->proc_ops->proc_open;
		rv = open(inode, file);

可见 proc_ops 中的open read write等函数应该都是通过 file_operations 中的open read write函数被调用的。

注意private data的使用:       

1. 在agile_proc_init() 中
    proc_create_data(file_name, 0644, proc_dir, &proc_fops, ap_dev); 
    函数参5指定private data.

2. agile_proc_open() 中
    single_open(file, agile_proc_read, PDE_DATA(inode)); 
    函数参3需要传递PDE_DATA(inode)。

3. agile_proc_read(struct seq_file *m, void *v) 中获取private data:
    struct agile_pci_device *ap_dev  = (struct agile_pci_device *)m->private;

4. 同理3,在
    agile_proc_write(struct file *fp,const char __user *u_buffer, 
            size_t count, loff_t *data) 
    中获取private data:
    struct agile_pci_device *ap_dev = ((struct seq_file *)fp->private_data)->private;

上述代码只列出了创建和使用proc文件的代码框架,细节在使用过程中根据需求进行改进和优化。

你可能感兴趣的:(嵌入式,linux驱动,proc,文件,linux)