Linux内核开发之简单字符设备驱动(上)

来源:互联网 发布:淘宝装修图 编辑:程序博客网 时间:2024/05/02 02:57
废话少说,先来介绍几个必须要知道的和字符设备有关的结构体,然后结合代码详细讲解。
第一部分    必要的设备结构体
1)linux 2.6内核中使用cdev结构体表示字符设备:struct cdev{  struct kobject kobj;//内嵌的kobject对象  struct module *owner;//所属模块  struct file_operations *ops;//文件操作结构体  struct list_head list;  dev_t dev;//设备号,长度为32位,其中高12为主设备号,低20位为此设备号  unsigned int count;};
可以使用下列宏从dev_t中获得主次设备号:                         也可以使用下列宏通过主次设备号生成dev_t:
    MAJOR(dev_t dev);                                                    MKDEV(int major,int minor);
    MINOR(dev_t dev);
说明:在2.6内核中可以容纳大量的设备,而先前的内核版本却限于255个主设备号和255个此设备号。
 
2)file_operations结构体中的成员函数是字符设备驱动程序设计中的主体内容,这些函数实际会在应用程序进行linux的open(),write(),read(),close()等系统调用时被最终调用。目前的file_operations结构已经变得非常大,在这里我们就关心和我这个设备程序有关的几个函数,以后用到了,咱们再提也不迟:
static const struct file_operations globalmem_fops ={   .owner= THIS_MODULE,   .llseek = globalmem_llseek,//修改一个文件的当前读写位置并将新位置返回,出错时,返回一个负值   .read = globalmem_read,   .write = globalmem_write,   .ioctl = globalmem_ioctl,//设备相关控制命令的实现,内核可以识别一部分控制命令(这时就不用调用ioctl                             ()),如果设备不提供这个函数,而内核又不识别该命令,则返回-EINVAL.   .open = globalmem_open,   .release = globalmem_release,};
 
3)file结构与用户空间的File没有任何关联,strcut file是一个内核结构,它不会出现在用户程序中。它代表一个打开的文件(不局限于设备驱动程序,系统中每个打开的文件在内核空间中都有一个对应的file结构)。它由内核在open时创建,并传递给在该文件进行操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会释放掉这个数据结构。
 
4)内核用它node结构在内部表示文件,其和file结构不同。后者表示打开的文件描述符。对于单个文件,可能会有很多表示打开的文件描述符的file结构,但它们都指向单个inode结构。在它里边和我们驱动程序有用的字段只有两个:
dev_t i_rdev;    //对表示设备文件的inode结构,该字段包含了真正的设备编号
struct cdev *i_cdev;     //是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。
我么可以使用下边两个宏从inode中获得主设备号和此设备号:
unsigned int iminor(struct inode *inode);unsigned int imajor(struct inode *inode);
为了程序的可移植性,我们应该使用上述宏,而不是直接操作i_rdev。
 
第二部分 源代码详解
必要的头文件

#define GLOBALMEM_SIZE 0X1000 /*全局内存大小4kb*/ 
#define MEM_CLEAR 0x1 //清零全局内存
#define GLOBALMEM_MAJOR 150 //预设的globalmem的主设备号
static globalmem_major = GLOBALMEM_MAJOR; 
 
//globalmem设备结构体
struct globalmem_dev
{
    struct cdev cdev;
    unsigned char mem[GLOBALMEM_SIZE];//全局内存
};
struct globalmem_dev *globalmem_devp;//设备结构指针//文件打开函数int globalmem_open(struct inode *inode, struct file *filp){filp->private_data= globalmem_devp;return 0;}//文件释放函数int globalmem_release(struct inode *inode, struct file *filp){return 0;}//globalmem_ioctl函数static int globalmem_ioctl(struct inode *inodep, struct file *filp,unsigned int cmd, unsigned long arg){struct globalmem_dev *dev = filp->private_data;switch (cmd){case MEM_CLEAR://清除全局内存memset(dev->mem, 0,GLOBALMEM_SIZE);printk(KERN_INFO "globalmem is set to zero\n");break;default:return - EINVAL;//其他不支持的命令}return 0;}//globalmem_read函数static ssize_t globalmem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct globalmem_dev *dev=filp->private_data;//获得设备结构指针//分析和获取有效的写长度if (p >= GLOBALMEM_SIZE)return count ? -ENXIO: 0;if (count > GLOBALMEM_SIZE-p)//如果要求读取的比实际可用的少count = GLOBALMEM_SIZE-p;if (copy_to_user(buf,(void *)(dev->mem + p),count)){ret = -EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "read %d byte(s) from %d",count,p);}return ret;}//globalmem_writestatic ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size; int ret = 0;struct globalmem_dev *dev = filp->private_data;//获得设备结构指针//分析和获取有效的写长度if( p >= GLOBALMEM_SIZE)return count ? -ENXIO: 0;if (count > GLOBALMEM_SIZE-p)//如果要求读取的比实际可用的少count = GLOBALMEM_SIZE-p;if (copy_from_user(dev->mem + p,buf, count))ret = -EFAULT;else{*ppos+= count;ret = count;printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);}return ret;}//globalmem_seek函数static loff_t globalmem_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;}

以上介绍了Linux简单字符设备中涉及到的基本而要特别重要的数据结构,还有就是源代码部分中有关file_operations中的所有操作,这些都将在应用程序进行Linux的

open(),write(),read(),close()等系统调用时最终被调用。这些都是从源代码中直接copy出来的,无论顺序还是结构都保持了实际代码的完整性,可以和下篇部分完整拷贝下来实际测试使用。

    
原创粉丝点击