字符设备驱动初阶

来源:互联网 发布:sql 去掉重复的行 编辑:程序博客网 时间:2024/05/18 02:58

字符设备驱动初阶学习

//以下为linux下的头文件

#include  <linux/module.h>   

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uacess.h>

 

#define CHARDRIVER_SIZE   0X1000     //全局最大内存4KB

#define MEM_CLR           0X01        //清零全局内存

#define CHARDRIVER_MAJOR    240      //预设字符设备的主设备号

 

static int  chardriver_major  = CHARDRIVER_MAJOR  ;

//chardriver 设备结构体

struct chardriver_dev {

                  struct cdev  cdev ;     //cdev结构体

                  unsigned char mem[CHARDRIVER_SIZE];    //全局内存

}

struct chardriver_dev   * chardriver_devp;  //设备结构指针

//文件打开函数

int chardriver_open(struct inode *inode , struct  file *filp)

{

                //将设备结构指针赋值给文件私有数据指针

               filp->private_data  = chardriver_devp;

               return 0;

}

//文件释放函数

int chardriver_release(struct inode *inode , struct file *filp)

{

         return 0;

}

//ioctl设备控制函数

static int chardriver_ioctl(struct inode *inode, struct file *filp , unsigned int cmd , unsigned long arg)

{

                //获得设备结构指针

               struct chardriver_dev * dev = filp->private_data;

               switch (cmd)  {

                case   MEM_CLR:

                           memset(dev->mem, 0 , CHARDRIVER_SIZE);

                           printk(KERN_INFO "chardriver is set to zero \n");

                           break;

                 default:

                          return -EINVAL;

                }

               return 0;

}

//读函数

static ssize_t chardriver_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 chardiver_dev *dev =  filp->private_data; 

                  //分析和获取有效的写长度

                  if(p > = CHARDRIVER_SIZE)    

                          return 0;

                   if(count > CHARDRIVER_SIZE - p)

                           count = CHARDRIVER- p;

                    

                 //内核空间的数据复制到用户空间

                if(copy_to_user(buf, (void *) (dev -> mem+ p) , count ))  

                         ret = - EFAULT;

                 else  {

                          *ppos += count;

                           ret  = count;

       

                            printk(KERN_INFO "read %u bytes from %lu\n",count , p);

                    }

                   return ret;

        }

//写函数

stacit ssize_t chardriver_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 chardriver_dev *dev = filp->private_data;

                 

                  //分析有效写长度

                  if(p>= CHARDRIVER_SIZE)

                         return  0;

                   if(count > CHARDRIVER_SIZE - p)

                        count = CHARDIVER_SIZE -p;

                   //将数据从用户空间发送到内核空间,确切的说应该是发送给设备

                   if(copy_from_user(dev->mem+p ,buf ,  conut))

                               ret = - EFAULT;

                    else {

                              *ppos += count;

                                ret = count;

                                printk(KERN_INFO "written %u bytes from %lu\n",count ,p);

                    }

                    return ret;

  }

 

       //seek文件定位函数

    static loff_t chardriver_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 >CHARDRIVER_SIZE )  {

                               ret = - EINVAL;

                               break;

                        }

                      filp ->f_pos = (unsigned int )offset;

                      ret = filp->f_ops;

                       break;

                     case 1: //相对于当前位置的偏移

                             if((filp->f_pos+offset) > CHARDRIVER_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 chardriver_fops = {

                  .owner = THIS_MODULE;

                  .llseek = chardriver_llseek;

                   .read = chardriver_read;

                   .write =chardriver_write;

                  .ioctl = chardriver_ioctl;

                  .open = chardriver_open;

                  .release = chardriver_release;

};

//创建cdev

static void chardriver_setup_cdev(struct chardriver_dev *devp , int index)

{

             int err , devno = MKDEV (chardriver_major , index);

              cdev_init (&dev->cdev , &chardriver_fops);

              dev->cdev.owner = THIS_MODULE;

              err = cdev_add(&cdev->cdev, devno , 1);

              if(err)

                     printk(KERN_NOTICE "errof %d adding chardriver %d\n",err ,index);

}

//设备驱动模块加载函数

int chardriver_init(void)

{

             int result ;

              dev_t devno = MKDEV(chardriver_major,0);

          //申请设备

              if(chardriver_major)

                  result = register_chrdev_region(devno , 1 ,"chardriver");

             else  {  //动态申请

                  result = alloc_chrdev_region(&devno , 0 , 1 ,"chardriver");

                  chardriver_major= MAJOR(devno);

               }

            if (result < 0)

                  return result ;

        //动态申请设备结构体的内存

        chardriver_devp = kmalloc(sizeof (struct chardriver_dev),GFP_KERNEL);

            if(!chardriver_devp)   {  //申请失败

                   result = - ENOMEM;

                   goto fail_malloc;

            }

          memset(chardriver_devp , 0 , sizeof (struct chardriver_dev));

           chardiver_setup_cdev(chardriver_devp,0);

           return 0;

fail_malloc:

              unreister_chrdev_region(devno, 1);

              return  result ;

   }

 

//函数卸载模块

void chardriver_exit(void )

{

            cdev_del(&chardriver_devp->cdev);  //删除cdev

             kfree (chardriver_devp);                   //释放设备结构体内存

             unregister_chrdev_region(MKDEV(chardriver_major,0),1) ; //注销设备号

}

//模块的一些版本、许可证、以及加载卸载函数注册与注销

MODULE_LICENSE("Dual BSD/GPL");

module_param(chardriver_major , int , S_IRUGO);

module_init(chardriver_init);

module_exit(chardriver_exit);

}

由此总结:字符设备驱动程序应该分为四部分

首先是设备结构体说明

struct chardriver_dev {

struct cdev cdev ; //cdev结构体

unsigned char mem[CHARDRIVER_SIZE]; //全局内存

}

其次是设备的操作函数

static const struct file_operations chardriver_fops = {

.owner = THIS_MODULE;

.llseek = chardriver_llseek;

.read = chardriver_read;

.write =chardriver_write;

.ioctl = chardriver_ioctl;

.open = chardriver_open;

.release = chardriver_release;

};

然后是字符设备的创建

static void chardriver_setup_cdev(struct chardriver_dev *devp , int index)

{

int err , devno = MKDEV (chardriver_major , index);

cdev_init (&dev->cdev , &chardriver_fops);

dev->cdev.owner = THIS_MODULE;

err = cdev_add(&cdev->cdev, devno , 1);

if(err)

printk(KERN_NOTICE "errof %d adding chardriver %d\n",err ,index);

}

最后是驱动模块的加载与卸载

module_init(chardriver_init);

module_exit(chardriver_exit);