简单Linux字符型驱动

字符型驱动设备

Linxu驱动程序整体分为三个类型:字符型设备驱动、块设备驱动和网络设备驱动,其结构图如下

简单Linux字符型驱动_第1张图片

首先学习一下最简单的字符型驱动的写法。字符驱动是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等
简单Linux字符型驱动_第2张图片

如上图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性。通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等。在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获取设备号,通过cdev_init( )建立cdev与file_operations之间的连接,通过cdev_add( )向系统添加一个cdev以完成注册。模块卸载函数通过cdev_del( )来注销cdev,通过unregister_chrdev_region( )来释放设备号。用户空间访问该设备的程序通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。
字符型驱动程序主要做如下三件事:
1. 定义一个结构体static struct file_operations变量,在其中定义一些设备的打开、关闭、读、写、控制函数。
2. 在结构体外分别实现结构体忠定义的这些函数
3. 向内核中注册和删除驱动模块
定义结构体的格式如下

static struct file_operations myDriver_fops = {
	.owner = THIS_MODULE,
	.write = myDriver_write,
	.read = myDriver_read,
	.ioctl = myDriver_ioctl,
	.open = myDriver_open,
	.release = myDriver_release,
};


实现write函数操作:

static char myDriver_Buffer[1024*1024]; // 模拟字符设备数据

static sszie_t  myDriver_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	sizeof_t fill_size = count;
	// 超出写入缓冲长度
	if(*f_pos >= sizeof(myDriver_Buffer))
	{
		*f_pos = sizeof(myDriver_Buffer);

		return 0;
	}

	if((count + *f_pos )>sizeof(myDriver_Buffer))
	{
		*fill_size = sizeof(myDriver_Buffer) - *f_ops;
	}

	// 将用户数据拷贝到内核空间
	copy_form_user(myDriver_Buffer[*f_ops], buf, fill_size);

	*f_ops += fill_size;

	return fill_size;
}

实现read函数操作:

static ssize_t myDriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	size_t read_size = count;

	if(*f_pos >= sizeof(myDriver_Buffer))
	{
		*f_pos = sizeof(myDriver_Buffer);

		return 0;
	}

	if((count + *f_pos )>sizeof(myDriver_Buffer))
	{
		read_size = sizeof(myDriver_Buffer) - *f_ops;
	}

	// 将内核空间数据拷贝到用户
	copy_form_user( buf, myDriver_Buffer[*f_ops], read_size);

	*f_ops += read_size;

	return read_size;
}


实现ioctl函数操作:

static int myDriver_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	// 模拟cmd命令值
	int cmd_0 = 0;
	int cmd_1 = 1;
	int cmd_2 = 2;
	int cmd_3 = 3;
	
	if(_IOC_TYPE(cmd) != TSTDRV_MAGIC)
	{
		return -ENOTTY;
	}

	if(_IOC_TYPE(cmd) > TSTDRV_MAXNR)
	{
		return -ENOTTY;
	}

	switch(cmd)
	{
		case cmd_0:
		{
			// 添加处理流程
				break;
		}

		case cmd_1:
		{
			// 添加处理流程
				break;
		}

		case cmd_2:
		{
			// 添加处理流程
				break;
		}

		case cmd_3:
		{
			// 添加处理流程
				break;
		}
	}

	return 0;
}



实现open函数

static int open(struct inode *inode, struct file *file)
{
	// 增加驱动程序的使用次数
	MOD_INC_USE_COUNT;
	return 0;
}

 
  
实现release函数

static int myDriver_release(struct inode *inode, struct file *filp)
{
	MOD_INC_USE_COUNT;

	return 0;
}

驱动程序初始化函数

static int myDriver_Major = 0;
static int __init myModule_init(void)
{
	// 模块初始化
	PRINTK("myMoudle_init\n");

	// 驱动注册 自动分配设备号
	myDriver_Major = register_chrdev(0, "myDriver", &myDriver_fops);
	
       if(myDriver_Major < 0)
       {
        	PRINTK("register char device fail!\n");
        	return myDriver_Major;
       }

    	PRINTK("register myDriver OK! Major = %d\n", myDriver_Major);
    	
#ifdef CONFIG_DEVFS_FS

	// 创建设备目录
    	devfs_myDriver_dir = devfs_mk_dir(NULL, "myDriver", NULL);

    	// 创建设备文件
    	devfs_myDriver_raw = devfs_register(devfs_myDriver_dir, "raw0", DEVFS_FL_DEFAULT, myDriver_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &myDriver_fops, NULL);

    	PRINTK("add dev file to devfs OK!\n");
    	
#endif
}

驱动程序退出函数


static void __exit myMoudle_exit(void)
{
	// 模块退出代码
	PRINTK("myModule_exit\n");

   	// 驱动注销
    if(myDriver_Major > 0)
    {
    
#ifdef CONFIG_DEVFS_FS
        devfs_unregister(devfs_myDriver_raw);
        devfs_unregister(devfs_myDriver_dir);
#endif
        unregister_chrdev(myDriver_Major, "myDriver");
        
    }
    return;

}


总结

开发字符设备驱动程序的基本步骤

1. 确定主设备号和次设备号

2. 实现驱动设备程序

(1) 实现file_operations结构体中的函数

(2) 实现初始化函数并注册字符设备

(3) 实现销毁函数并释放字符设备

3. 创建设备文件节点





SL--希望你能每天开心

你可能感兴趣的:(Linux)