[Linux驱动]字符设备驱动学习笔记(二)———实例
一,注册字符设备
- #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()两个函数除了在内核空间和用户空间进行数据拷贝,还会检查用户空间的指针是否有效,如果指针无效则不拷贝,如果拷贝过程中遇到无效地址,则仅仅会复制部分数据,,在这两种情况下返回值是还需要拷贝内存数量的值。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。