字符设备的编写二

字符设备编写二


前言

上一篇文章介绍了一下字符设备编写的步骤,仅此而已,想要真正掌握字符设备编写,需要对其包含的数据结构有所掌握,这才是重要的。


一、字符设备的数据结构

请添加图片描述

图片来源于网络,一直以来有想自己画一下,以后有时间将上图换成我自己画的。

二、数据结构分析

编写字符设备需要实现file_operations接口,在APP打开文件时,可以得到一个整数,这个整数被称为文件句柄。对于 APP的每一个文件句柄,在内核里面都有一个“ struct file”与之对应。

struct file {
	union {
		struct llist_node	fu_llist;
		struct rcu_head 	fu_rcuhead;
	} f_u;
	struct path		f_path;
#define f_dentry	f_path.dentry
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;

	/*
	 * Protects f_ep_links, f_flags.
	 * Must not be taken from IRQ context.
	 */
	spinlock_t		f_lock;
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	struct mutex		f_pos_lock;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

在这里插入图片描述

open 打开文件时,传入的 flags、 mode 等参数会被记
录在内核中对应的 struct file 结构体里(f_flags,f_mode)去读写文件时,文件的当前偏移地址也会保存在 struct file 结构体的f_pos 成员里。打开字符设备节点时,内核中也有对应的 struct file。这个结构体中的结构体:

struct file_operations *f_op;

这是由驱动程序提供的。

在这里插入图片描述


总结

字符设备就暂时学习到这,后面将继续为大家分享。

附录

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/slab.h>

/* 寄存器物理地址 */
#define CCM_CCGR1_BASE				(0X020C406C)	
#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)
#define GPIO1_DR_BASE				(0X0209C000)
#define GPIO1_GDIR_BASE				(0X0209C004)

/* 映射后的寄存器虚拟地址指针 */
static volatile unsigned int  *IMX6U_CCM_CCGR1;
static volatile unsigned int  *SW_MUX_GPIO1_IO03;
static volatile unsigned int  *SW_PAD_GPIO1_IO03;
static volatile unsigned int  *GPIO1_DR;
static volatile unsigned int  *GPIO1_GDIR;

//静态指定

static	unsigned int dev_major;
static	struct class *cls;
static	struct device *dev;
	/* 映射后的寄存器虚拟地址指针 */


static int kernel_val = 555;
//read(fd,buf,size);
ssize_t chr_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
	int ret;
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	ret = copy_to_user(buf, &kernel_val, count);
	if(ret > 0)
	{
		printk("failed copy_to_user\r\n");
		return -EFAULT;
	}

	return 0;
}

ssize_t chr_drv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fops)
{
	int ret;
	int value;
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	ret = copy_from_user(&value, buf, count);
	if(ret > 0)
	{
		printk("failed copy_from_user\r\n");
		return -EFAULT;
	}
	
	if(value){
		*GPIO1_DR |= (1 << 3);
	}else{
		*GPIO1_DR &= ~(1 << 3);
	}
	
	return 0;
}

int chr_drv_open (struct inode *inode, struct file *filp)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	return 0;
}

int chr_drv_close (struct inode *inode, struct file *filp)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	return 0;
}

const struct file_operations my_fops  = {
	.open = chr_drv_open,
	.read = chr_drv_read,
	.write = chr_drv_write,
	.release = chr_drv_close,
};

/*
②在模块加载入口函数中
	a  申请主设备号(内核中用于区分和管理不同字符设备)
		register_chrdev()
	b  创建设备节点文件(为用户提供一个可操作到文件接口--open())
		struct class *class_create()
		struct device *device)create()
	c  硬件初始化
		1.地址映射
		2.中断申请
		3.实现硬件的寄存器的初始化
	d  实现file_operation
*/
static int __init chr_dev_init(void)
{
	int ret;
/*
	//0 实例化全局的设备对象
	led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
	if(led_dev == NULL);
	{
		printk("kmalloc failed\r\n");
		return -ENOMEM;
	}
*/
	//一般都是申请设备号资源
	//1.申请设备号
	dev_major = register_chrdev(0, "chr_dev_test", &my_fops);
	if(dev_major < 0){
		printk(KERN_ERR"register_chrdev failed\n");
		return  -ENODEV;

	}

	//2.创建设备文件
	cls = class_create(THIS_MODULE, "chr_cls");
	if(IS_ERR(cls))
	{
		printk(KERN_ERR"class_create failed\n");
		ret = PTR_ERR(cls);//将指针出错原因转换为一个出错码
		goto err_1;
	}
	// /dev/led0
	dev = device_create(cls, NULL, MKDEV(dev_major, 0), NULL, "led%d", 0);
	if(IS_ERR(dev))
	{
		printk(KERN_ERR"device_create failed\n");
		ret = PTR_ERR(dev);//将指针出错原因转换为一个出错码
		goto err_2;
	}
	//3,硬件初始化
		/* 初始化LED */
	/* 1、寄存器地址映射 */
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);

	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);

  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);

	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);

	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);


	/* enable GPIO1
	 * configure GPIO1_io3 as gpio
	 * configure GPIO1_io3 as output 
	 */
	*IMX6U_CCM_CCGR1 |= 0x0C000000;
	
	*SW_MUX_GPIO1_IO03 &= 0xfffffff0;
	*SW_MUX_GPIO1_IO03 |= 0x5;

	*SW_PAD_GPIO1_IO03 &= 0xfffffff0;
	*SW_PAD_GPIO1_IO03 |=  0x10B0;


	*GPIO1_GDIR &= 0xfffffff0;
	*GPIO1_GDIR |= 0x8;


/*
readl   writel
static inline u32 readl(const volatile void __iomem *addr)

static inline void writel(u32 value, void *addr)
 2、使能GPIO1时钟 
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	 清楚以前的设置 
	val |= (3 << 26);	设置新值 
	writel(val, IMX6U_CCM_CCGR1);

	 3、设置GPIO1_IO03的复用功能,将其复用为
	    GPIO1_IO03,最后设置IO属性。
	 
	writel(5, SW_MUX_GPIO1_IO03);
	
	寄存器SW_PAD_GPIO1_IO03设置IO属性
	 bit 16:0 HYS关闭
	 bit [15:14]: 00 默认下拉
     bit [13]: 0 kepper功能
     bit [12]: 1 pull/keeper使能
     bit [11]: 0 关闭开路输出
     bit [7:6]: 10 速度100Mhz
     bit [5:3]: 110 R0/6驱动能力
     bit [0]: 0 低转换率
	
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	 4、设置GPIO1_IO03为输出功能 
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	 清除以前的设置 
	val |= (1 << 3);	 设置为输出 
	writel(val, GPIO1_GDIR);

	 5、默认关闭LED 
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);
或
writel(readl(led_dev->GPIO1_DR) | (1 << 3),led_dev->GPIO1_DR);


*/
	
	return 0;

err_2:
	class_destroy(cls);
err_1:
	unregister_chrdev(dev_major, "chr_dev_test");

	return ret;
}

static void __exit chr_dev_exit(void)
{
	//一般都是释放资源
		/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);
	
	device_destroy(cls, MKDEV(dev_major, 0));
	class_destroy(cls);
	unregister_chrdev(dev_major, "chr_dev_test");

	

}
//①实现模块的加载和卸载入口函数
module_init(chr_dev_init);
module_exit(chr_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lfp");


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值