一,注册字符设备
#define GLOBALMEM_MAJOR 256
#define GLOBALMEM_SIZE 0X1000 //4k
static int char_major=GLOBALMEM_MAJOR;//主设备号
struct chartest_dev
{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};
struct chartest_dev glob_char_dev;//全局设备结构
static struct class *char_class=NULL;
static struct class_device *char_class_dev=NULL;
//-------------------------------------设备打开函数,匹配上层函数调用
static int char_open(struct inode *inode, struct file *filp)
{
filp->private_data = &glob_char_dev;
return 0;
}
//-------------------------------------从内核往用户空间拷贝读取数据
static ssize_t char_read(struct file* filp,char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p=*ppos;
int ret=0;
struct chartest_dev *char_dev=filp->private_data;
if(p>GLOBALMEM_SIZE)//作越界检查
return 0;
if(count>GLOBALMEM_SIZE-p)// 读取数据较多
count=GLOBALMEM_SIZE-p;
if(copy_to_user(buf,(void*)(char_dev->mem+p),count))
ret=-EFAULT;
else
{
*ppos=*ppos+count;
ret=count;
printk(KERN_INFO "READ %d BYTES(S) FROM %d",count,p);
}
return ret;
}
//-------------------------------------从用户控件往内核写数据
static ssize_t char_write(struct file* filp,const char __user *buf,size_t count,loff_t *ppos)
{
unsigned long p=*ppos;
int ret=0;
struct chartest_dev *char_dev=filp->private_data;
if(p>GLOBALMEM_SIZE)
return 0;
if(count>GLOBALMEM_SIZE-p)
count=GLOBALMEM_SIZE-p;
if(copy_from_user(char_dev->mem+p,buf,count))
ret=-EFAULT;
else
{
*ppos=*ppos+count;
ret=count;
printk(KERN_INFO "WRITTEN %d BYTES(S) FROM %d",count,p);
}
return ret;
}
static loff_t char_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig)
{
case 0:
if (offset < 0)
{
ret = - EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE)
{
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
{
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
static const struct file_operations char_fops = {
.owner = THIS_MODULE,
.llseek =char_llseek,
.open = char_open,
.read = char_read,
.write = char_write,
};
static void chartest_setup_cdev()
{
int err,devno=MKDEV(char_major,0);
cdev_init(&glob_char_dev.cdev,&char_fops);/*初始化*/
glob_char_dev.cdev.owner=THIS_MODULE;
err=cdev_add(&glob_char_dev.cdev,devno,1);
if(err<0)
printk(KERN_NOTICE "ERROR %d ADDING chartest",err);
char_class=class_create(THIS_MODULE,"chartest");
char_class_dev=device_create(char_class,NULL,devno,NULL,"chartest",0);
}
static int char_init(void)
{
int result;
unsigned long char_dev_no=MKDEV(char_major,0);
if(char_major){
result=register_chrdev_region(char_dev_no,1,"chartest");
}
else{
result=alloc_chrdev_region(&char_dev_no,0,1,"chartest");
char_major=MAJOR(char_dev_no);
}
printk(KERN_ALERT "Hello,,We succeed\n");
if(result<0)
return result;
chartest_setup_cdev();
printk(KERN_ALERT "Hello,,We succeed\n");
return 0;
}
static void char_exit(void)
{
cdev_del(&glob_char_dev.cdev);
unregister_chrdev_region( MKDEV(char_major,0),1);//ZHU XIAO
device_unregister(char_class_dev);
class_destroy(char_class);
printk(KERN_ALERT "720 unregister success\n");
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(char_init);
二,注意点:
read和write函数的参数 buff是用户空间的指针,因此内核代码不能直接引用其中的内容,主要原因如下:
1,在内核模式运行时,用户空间的指针可能是无效的
2,用户空间是分页的,涉及的内容可能不在RAM中,对用户空间的内存直接引用可能导致页错误
3.指针是用户空间提供的,可能存在缺陷。
copy_to_user()和copy_from_user()两个函数除了在内核空间和用户空间进行数据拷贝,还会检查用户空间的指针是否有效,如果指针无效则不拷贝,如果拷贝过程中遇到无效地址,则仅仅会复制部分数据,,在这两种情况下返回值是还需要拷贝内存数量的值。