文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>linux字符设备驱动

linux字符设备驱动

时间:2010-11-28  来源:linuxyant

   必要的设备结构体
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 //清零全局内存
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_write
static 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出来的,无论顺序还是结构都保持了实际代码的完整性,可以和下篇部分完整拷贝下来实际测试使用。

排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载