字符设备驱动相关内容



1.关于设备文件的介绍(翻译自Understanding the Linuxkernel



设备文件

在类Unix系统中基于一个概念:一切皆文件,文件就像是包含一系列数据的信息容器。因此,I/O设备在类Unix系统中被当成特殊的文件进行处理,称为设备文件;系统调用可以同时操作设备文件和普通文件。例如,write函数可以向普通文件中写入数据,也能通过向/dev/lp0设备文件中写入数据将数据发送至打印机。

根据设备驱动的特性,设备文件可以分为两大类:块设备文件和字符设备文件。这两种硬件设备的分类可能没那么明确,但是我们可以认为其区别如下:

块设备上的数据可以随机存取,传输一个数据块时间很短且几乎相同,传统的块设备有硬盘,软盘,CD-ROMDVD

对于块设备的数据的操作,理论上来说是不支持随机读写的(例如声卡),即便能够随机读写,它们读取设备文件中特定数据的时间极大地依赖于其在设备的位置(如磁带播放器)

网卡设备不适用此分类,因为网卡设备不直接和设备文件打交道。

在早期的类Unix操作系统中就已经有设备文件了,设备文件通常是文件系统中真实的文件,设备文件的inode节点都包含了相关字符设备和块设备的标识符,inode节点中没有指向硬盘数据的指针(文件数据),因为它们根本没有文件数据。

通常,设备文件标识符包括设备文件的类型(字符设备或块设备)以及设备号。设备号又分为主设备号和次设备号,主设备号表示设备类型,相同类型设备主设备号相同,且共享一套文件操作方法(file operation)。次设备号用于区分多个相同类型设备。

Unix操作系统中可以用mknod()系统调用来创建设备文件,其参数有设备类型(c代表字符设备,b代表块设备),主设备号和次设备号。设备文件通常位于目录/dev/下,值得注意的是字符设备和块设备的设备号是相互独立的,例如字符设备(3,0)和块设备(3,0)两者是完全不同的两个设备。

设备文件通常都对应真实的硬件设备(如硬盘设备文件/dev/hda),或硬件设备的物理或逻辑分区(如硬盘分区文件/dev/hda2)。然而,有些情况下,设备文件并不是对应真实的物理设备而是虚拟的逻辑设备,例如/dev/null设备文件,其作用是丢弃所有的输入数据,因此该文件总是为空。

 

字符设备驱动


操作一个字符设备相对比较容易,它没有块设备中的缓冲机制和硬盘缓存。然而,根据不同的功能,字符设备之间也会大相径庭,有的字符设备需要一套复杂的通讯协议来操作设备,而有的字符设备却只需要从I/O口中读取几个字符,如一个多串口卡设备的设备驱动就远比一个USB鼠标复杂。



2.自己实现一个简单的字符设备驱动

在这个字符设备驱动中,我一共实现了四个接口分别为openreadwrite以及release函数,在上层应用中可通过open,read,write以及close函数调用,模块的入口是mychrdev_init函数,即加载模块时调用的函数,函数中通过register_chrdev函数注册该字符设备驱动,unregister_chrdev函数卸载该字符设备驱动。下面分别贴上设备驱动源代码以及测试文件代码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <asm/uaccess.h>
#include <linux/kmod.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <asm/traps.h>
static char buf[32];
static int major;
static char Device_open;
#define ERROR -1;
static int mychrdev_open(struct inode *inode,struct file *filep){
	if(Device_open){	
	return -EBUSY;
	}
	Device_open++;
	printk("The major number of the device is %d\n", MAJOR(inode->i_rdev));
	printk("The minor number od the device is %d\n", MINOR(inode->i_rdev));
	printk("<0>module_refcount(module):%d\n",module_refcount(THIS_MODULE));
	try_module_get(THIS_MODULE);
	printk("<0>module_refcount(module):%d\n",module_refcount(THIS_MODULE)); 
	return 0;
}
static ssize_t mychrdev_read(struct file *fillp, char __user *data, size_t count, loff_t *off){
	#ifdef DEBUG
		printk(KERN_INFO "device_read(%p,%p,%d)\n", filep, buf, count);
	#endif
	
	printk(KERN_INFO "entering the read function\n");
	//printk("%s\n",data);
	if(copy_to_user(data,buf,sizeof(buf))){
	printk("cannot copy data to usersapce\n");
	return ERROR;
	}
	#ifdef DEBUG
		printk(KERN_INFO "Read %d bytes, %d left\n", i, count);
	#endif
	return 0;
	
	/*if(copy_to_user(data,buf,sizeof(buf))){
	printk("cannot copy data to usersapce\n");
	return ERROR;
	}
	return 0;*/
}

static ssize_t mychrdev_write(struct file *filp, const char __user *data, size_t count, loff_t *off){
	//static int i;
	#ifdef DEBUG
		printk(KERN_INFO "device_write(%p,%p,%d)\n", filep, buffer, count);
	#endif
	
	printk("entering the write function\n");
	printk("%s\n",data);
	if(copy_from_user(buf,data,sizeof(data)))
	{ printk("cannot copy data from userspace\n");
		return ERROR;
}
	return 0;
}
static int mychrdev_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
	printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
#endif

	
	Device_open--;

	module_put(THIS_MODULE);
	return 0;
}


struct file_operations my_op={
	.owner=THIS_MODULE,
	.open=mychrdev_open,
	.read=mychrdev_read,
	.write=mychrdev_write,
	.release=mychrdev_release,
	
};

static int mychrdev_init(void)
{	
	static int major;
	major = register_chrdev(0,"zhaichuan",&my_op);
	if (major < 0) {
		printk(KERN_ERR "unable to get major %d\n", major);
		return major;
	}
	printk(KERN_INFO "%s The major device number is %d.\n",
	       "Registeration is a success", major);
	printk(KERN_INFO "please use mknod %s c %d 0 to create a device file\n", "zhaichuan", major);
	
	return 0;
	}
static int mychrdev_exit(void)
{	//static int ret;	
	unregister_chrdev(major, "zhaichuan");
	
	/* 
	 * If there's an error, report it 
	 */
	printk("<0>module_refcount(module):%d\n",module_refcount(THIS_MODULE));
	return 0;
	}

module_init(mychrdev_init);
module_exit(mychrdev_exit);
MODULE_LICENSE("GPL");
测试文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
//#include <fcntl.h>
#include <string.h>
#include <stdio.h>

#include <io.h>
#include <asm-generic/ioctl.h>
#include <asm-generic/fcntl.h>
#define LENGTH 80
char buffer[LENGTH];
/*int strlen(char *s){
	int n=0;
	while(s++){
	n++;
	}
return n;
}*/
int main(int argc,char* argv[])
{static int i;
static int ret;
static int ret2;
static char *s;
char *m="LInux character device";
//memcpy(s,m,strlen(m));
int fd;
//fd = open("/home/zhaichuan/char.c",O_WRONLY);
//printf("%d\n",fd);
fd=open("/dev/zhaichuan",O_WRONLY);
printf("%d\n",fd);
if(fd==-1){
	printf("cannot open device\n");
	return 0;
}
printf("CHARDEV open successfully\n");
ret2=write(fd,m,10);
printf("The return value of write function is %d\n",ret2);
printf("CHARDEV write successfully\n");
close(fd);
printf("%s\n",m);
fd=open("/dev/zhaichuan",0);
ret=read(fd,buffer,sizeof(buffer));
printf("The return value of read function is %d\n,and the returning value of fd is %d\n",ret,fd);
close(fd);
printf("%s\n",buffer);
return 0;
}

调试时遇到的问题:


1.测试文件执行时,write函数返回-1,解决方法:在执行测试文件时,一定要获得管理员权限,如sudo ./mychar.c,不然文件是不能写入的,之前一直纠结了很久。


2.文件中定义了的函数未包含头文件,解决办法:可以在头文件目录中使用grep命令搜索相对应的函数名,找到对应的头文件名,而后再包含进你的文件中。


3.多用print命令打印调试,我的文件中有很多的printf/printk,很多都是为了调试而后写入文件的。







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值