6.0 内核驱动-字符型驱动

本文深入探讨了裸机驱动与Linux内核驱动的区别,主要集中在Linux操作系统如何通过接口和系统框架增强驱动功能,如同步机制、电源管理等。同时,介绍了Linux字符驱动从0.11到3.4版本的演变,展示了从简单的file_operations结构到cdev的使用,提高了设备号的利用率。文章还详细解析了驱动注册、设备节点创建和管理,以及如何通过class和class_device结构管理设备。
摘要由CSDN通过智能技术生成

1 裸机驱动和linux驱动的区别

相同:都要进行硬件实现层的设计(硬件裸机驱动)
不同:

  1. linux操作系统中加了众多接口,从系统API到硬件实现层调用过程中的系统框架
  2. 同步,互斥,POLL,并发, 自旋锁,信号量 ,等待队列 standby,;linux机制同样要体现在内核驱动中
  3. 驱动要服从linux内核管理:设备树,电源管理的全局构架
  4. 不同CPU或不同linux版本的移植

2 linux的架构

在这里插入图片描述

2.1 linux0.11 字符驱动

下面是从用户层的系统调用,到虚拟文件系统,到真是驱动函数的过程
在这里插入图片描述

2.2 linux2.6 字符驱动

__register_chardev_region
把创建传进来的file_operations打包成char_device_struct
并且把这个变量放到 全面变量 chrdevs数组中 设备号 指定的位置中

一个设备(major,minor)最多对应256个major
并且每一个major只能对应一个fops

使用:

与0.11结构完全一样,不一样的是堆结构体对象进行的封装和扩充
在这里插入图片描述

static struct file_opeations Tchardrv_fops = {
    /* data */
    .owner = THIS_MODULE,
    .read = ReadChar,
};
//init前加__, 会把这部分代码加载到指定的段中 #define __init __attribute__ ((__section__ (".text.init"))), 运行完后,系统会自动把这个段释放掉
static int major 
static __init InitChardrv(void)
{
   // int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
    major = register_chrdev(major, "char_devfisrt", &Tchardrv_fops)
}

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	//帧缓冲设备中,如果使用定义了MMAP,则可以在应用程序中进行对应的帧缓冲读写,不需要做用户到操作系统内核的内存拷贝
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);//打开设备,对应insmod
	int (*flush) (struct file *, fl_owner_t id);//冲刷帧
	int (*release) (struct inode *, struct file *);//释放设备,对应rmmod
	//同步函数
	int (*fsync) (struct file *, struct dentry *, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*dir_notify)(struct file *filp, unsigned long arg);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
};

经过系统调用,chardevs在这个全局结构体数组找到对应major的char_device_struct

static struct char_device_struct {
	struct char_device_struct *next;
	unsigned int major; //设备号
	unsigned int baseminor;
	int minorct;
	char name[64];
	struct file_operations *fops;//设备的各种操作结构函数
	struct cdev *cdev;		/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

__register_chrdev_region函数会把char_device_struct添加到chrdevs中去

用到__register_chrdev_region函数的有
在这里插入图片描述

编写示例驱动
在自己的驱动中使用register_chrdev来进行注册,来添加char_device_struct

//字符设备,GPIO LED设置

static struct class *first_chardev_class;
static struct class_device *first_chardev_class_dev_0;
static struct class_device *first_chardev_class_dev_1;
static int major;
//配置寄存器
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


ssize_t read_fisrt (struct file *file, char __user * buff, size_t count, loff_t *pos)
{

}

//字符设备的写函数,用于控制GPIO LED的开关,1-灯亮 0-灯灭
ssize_t write_fisrt (struct file *file, char __user * buff, size_t count, loff_t *pos)
{
    int val;
    copy_from_user(&val, buff, count);

    if (1 == val)
    {
        *gpfdata &= ~(0x1<<(4*2) | 0x1<<(5*2) | 0x1<<(6*2));
    }
    else
    {
        *gpfdata |= (0x1<<(4*2) | 0x1<<(5*2) | 0x1<<(6*2));
    }

    return 0;
}

static int open_first(struct inode *inode, struct file *file)
{
    printk("inode->irdev = %d\n", inode->i_rdev);
    *gpfcon &= ~(0x3<<(4*2) | 0x3<<(5*2) | 0x3<<(6*2));
    *gpfcon |= (0x1<<(4*2) | 0x1<<(5*2) | 0x1<<(6*2));
    printk("open is ok\n");
}


struct file_operations fisrt_char
{
    .owner = THIS_MODULE,       
    .read = read_fisrt,
    .write = write_fisrt,
    .open = open_first, 
};



static int __init InitChardrv_first(void)  //加上__init,放到.init_text段中 运行完后会这个段会被释放掉。不加的话,这个函数会放在其他位置,运行后也不释放
{
    //int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
    //注册一个fops
    major = register_chrdev(major, "fisrt_char_dev", &fisrt_char); //这里name是设备驱动的名字,和/dev中名字不一样,dev下名字是文件节点
    //可以手动使用mknod在/dev下创建一个节点,然后挂接这个设备。也可以直接用下面函数自动创建节点
    //创建一个主设备号对应的设备节点
    first_chardev_class = class_create(THIS_MODULE, "first_chrdev");
    //创建多个设备节点的子设备 minor, 这里多个从设备对应同一个fops,就是操作同一个设备
    first_chardev_class_dev_0 = class_device_create(first_chardev_class, NULL, MKDEV(major, 0), NULL, "firstchar_0");//dev_t devt是major和minor的综合体MKDEV(major, minor)
    first_chardev_class_dev_1 = class_device_create(first_chardev_class, NULL, MKDEV(major, 1), NULL, "firstchar_1");
     
    //硬件相关操作
    //把物理地址映射到线性地址上去
    gpfcon = ioremap(0x56000000, 32);
    gpfdat = gpfcon + 1;

    return 1;
}

static int __exit ExitChardrv_first(void)//加上__exit, 放到.exit_text段中 运行完后会这个段会被释放掉。
{
    unregister_chrdev(major, "fisrt_char_dev");
    class_device_destroy(first_chardev_class_dev_0, MKDEV(major, 0));
    class_device_destroy(first_chardev_class_dev_1, MKDEV(major, 1));
    class_destroy(first_chardev_class);

    iounmap(gpfcon);
    return 1;    
}

module_init(InitChardrv_first); //把函数映射到指定的表中, 在insmod命令时通过表找这个函数
module_exit(ExitChardrv_first); //把函数映射到指定的表中, 在rmmod命令时通过表找这个函数

代码中创建class和class_device类似于创建了下面的结构:
主设备号对应class,次设备号对应class_device。
通过这个结构可以方便的通过设备号来找到对应的fops,一个主设备号通过class指定一个fops,剩下所有的class_device都指向这一个fops
在这里插入图片描述

2.3 linux3.4 字符驱动

上面会导致资源浪费,为了解决这个问题,使用cdev
cdev_region(major, minor, size, fops1)
一个major下的minor到minor+size的所有从设备对应fops1

其中chrdevs数组中每个成员都的内容改变了,添加了cdev,会指定某个范围的子设备号指向一个ops,而不再是一个主设备号下所有的子设备号都指向一个ops。增加子设备号了使用率。

上层再调用的时候,从根据major在数组中找到成员,在根据次设备号minor获取指定cdev,再从cdev中找到fops

在这里插入图片描述
添加cdev的方式
alloc_chrdev_region() ; //还没有分配任何cdev,用这个函数来分配。(major = 0 说明是第一次分配)
register_chrdev_region()//前面分配了,就调用这个函数来添加新的cdev。(major!= 0,说明已经分配了)

下面是示例代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stat.h>
#include <linux/cdev.h>

#include <asm/uaccess.h>
#include <asm/io.h>


#define MEM_MINORCNT 10
#define KINDLEMEM_SIZE 1024

//命令是一个32位数,由方向,类型,数据,大小组成
//#define _IOC(dir,type,nr,size) \
//        (((dir)  << _IOC_DIRSHIFT) | \
//         ((type) << _IOC_TYPESHIFT) | \
//         ((nr)   << _IOC_NRSHIFT) | \
//         ((size) << _IOC_SIZESHIFT))
#define KINDLE_MAGIC    'k'
#define KINDLE_MEM_CLEAR  _IOC(1, KINDLE_MAGIC, 0, 0)


#define OFFSET_HEAD     0
#define OFFSET_CURRENT  1


static int major;

struct kindlemem {
    struct cdev cdev;
    unsigned char mem[KINDLEMEM_SIZE];
};

struct kindlemem *kindlemem_p;

static ssize_t read_kindlemem(struct file *file, char __user *buf, size_t count, loff_t *off)
{
   int ret = 0;
   unsigned long pos = *off;
   unsigned long size = count;
   //获取文件结构体中私有数据
   struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;

   if (pos >= KINDLEMEM_SIZE)
        return 0;
   if (size > KINDLEMEM_SIZE - pos)
        size = KINDLEMEM_SIZE - pos;
   if (copy_to_user(buf, (kindlemem_tmp->mem) + pos, size))
   {
        return -EFAULT;
   }
   else
   {
        *off += size;
        ret = size;
        printk(KERN_INFO,"read size = %d frome %d\n", size, *off);
   }

   return ret;
}

static ssize_t write_kindlemem(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off)
{
   int ret = 0;
   unsigned long pos = *off;
   unsigned long size = count;
   //获取文件结构体中私有数据
   struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;
   
   if (pos >= KINDLEMEM_SIZE)
        return 0;
   if (size > KINDLEMEM_SIZE - pos)
        size = KINDLEMEM_SIZE - pos;
   if (copy_from_user(kindlemem_tmp->mem + pos, userbuf, size))
   {
        return -EFAULT;
   }
   else
   {
        *off += size;
        ret = size;
        printk(KERN_INFO,"read size = %d frome %d\n", size, *off);
   }
}

static long ioctl_kindlemem (struct file *file, unsigned int cmd, unsigned long arg)
{
   //获取文件结构体中私有数据
   struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;

   switch (cmd)
   {
       case KINDLE_MEM_CLEAR:
            memset(kindlemem_tmp->mem, 0, KINDLEMEM_SIZE);
            printk(KERN_INFO, "clear mem\n");
            break;
       default:
            return -EINIT;
  }
}

static loff_t llseek_kindlemem (struct file *file, loff_t offset, int origin)
{
    loff_t ret = 0;
    switch (origin)
    {
        case OFFSET_HEAD: //从开始移动
            if(offset < 0 || (unsigned int)offset > KINDLEMEM_SIZE)
            {
                ret = -EINVAL;
                break;
            }
            file->f_pos = (unsigned int)offset;
            ret = file->f_pos;
            break;
        case OFFSET_CURRENT:
            if((file->f_pos + offset) < 0  || (unsigned int)(file->f_pos + offset) > KINDLEMEM_SIZE))
            {
                ret = -EINVAL;
                break;
            }
            file->f_pos += (unsigned int)offset;
            ret = file->f_pos;
            break;
            
       default:
        ret = -EINVAL;
        break;
    }
    return ret;
}

static int open_kindlemem(struct inode *inode, sturct file *file)
{
    file->private_data = kindlemem_p;
    return 0;
}


int release_kindlemem (struct inode *inode, struct file *file)
{
    return 0;
}


static struct file_operations kindlemem_fops = {
    .owner = THIS_MODULE,
    .read = read_kindlemem,
    .write = write_kindlemem,
    .llseek = llseek_kindlemem,
    .open = open_kindlemem,
    .release =release_kindlemem,
    .unlock_ioctl = ioctl_kindlemem,
 };


//进行cdev的初始化配置
void setup_kindle_cdev(struct kindlemem *dev, int minor)
{
    int error;
    dev_t devno = MKDEV(major, minor);
    cdev_init(&dev->cdev, &kindlemem_fops);//初始化一个cdev,对内部成员ops进行赋值
    dev->cdev.owner = THIS_MODULE;
    error = cdev_add(&dev->cdev, devno, MEM_MINORCNT);//将初始化完成的cdev根据设备号devno添加到相应的位置
    if (error)
    {
        printk(KERN_NOTICE "ERROR %d %d", devno, minor);
    }
    memset(dev->mem, 0, KINDLEMEM_SIZE);
}


static int __init init_kindlemem(void )
{
    int ret;
    dev_t devno = MKDEV(major, 0);

    if(major)
    {
      //register_chrdev_region(dev_t from 从哪开始, unsigned count区域个数, const char * name)
      //给major下minor0-10创建一个cdev
      ret = register_chrdev_region(devno, MEM_MINORCNT, "kindlemem");
    }
    else
    {
        //baseminor:起始位置
       // alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name) 
       ret = alloc_chrdev_region(&devno, 0, MEM_MINORCNT, "kindlemem");
       major = MAJOR(devno);//上面函数会堆major复制,这里用这个函数来提取
    }
    kindlemem_p = kzalloc(sizeof(struct kindlemem), GFP_KERNEL);
    if (!kindlemem_p)
    {
        ret = -1;
        goto fail_kzalloc;
    }
    setup_kindle_cdev(kindlemem_p, 0);
    
    if (ret < 0)
        goto fail;
    return ret;
    fail:
        printk(KERN_ERR "kindlmem: failed to register chrdev region\n");
        return ret;
    fail_kzalloc:
        printk(KERN_ERR "kindlmem: failed to alloc memory\n");
        unregister_chrdev_region(devno, MEM_MINORCNT);
        return ret;
}


static int __exit exit_kindlemem(void )
{
    cdev_del(&kindlemem_p->cdev);
    kfree(kindlemem_p);
    unregister_chrdev_region(devno, MEM_MINORCNT);
}


module_init(init_kindlemem);
module_exit(exit_kindlemem);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("David Howells <dak@qq.com>");

当将注册多个cdev时,需要做初始化进行一些修改

   #include <linux/kernel.h>
   #include <linux/module.h>
   #include <linux/init.h>
   #include <linux/types.h>
   #include <linux/stat.h>
   #include <linux/cdev.h>
   
   #include <asm/uaccess.h>
   #include <asm/io.h>
   
   
   #define MEM_MINORCNT 10
   #define KINDLEMEM_SIZE 1024
   
   //命令是一个32位数,由方向,类型,数据,大小组成
   //#define _IOC(dir,type,nr,size) \
   //        (((dir)  << _IOC_DIRSHIFT) | \
   //         ((type) << _IOC_TYPESHIFT) | \
   //         ((nr)   << _IOC_NRSHIFT) | \
   //         ((size) << _IOC_SIZESHIFT))
   #define KINDLE_MAGIC    'k'
   #define KINDLE_MEM_CLEAR  _IOC(1, KINDLE_MAGIC, 0, 0)
   
   
   #define OFFSET_HEAD     0
   #define OFFSET_CURRENT  1
   
   
   static int major;
   
   struct kindlemem {
       struct cdev cdev;
       unsigned char mem[KINDLEMEM_SIZE];
   };
   
   struct kindlemem *kindlemem_p;
   
   static ssize_t read_kindlemem(struct file *file, char __user *buf, size_t count, loff_t *off)
   {
      int ret = 0;
      unsigned long pos = *off;
      unsigned long size = count;
      //获取文件结构体中私有数据
      struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;
   
      if (pos >= KINDLEMEM_SIZE)
           return 0;
      if (size > KINDLEMEM_SIZE - pos)
           size = KINDLEMEM_SIZE - pos;
      if (copy_to_user(buf, (kindlemem_tmp->mem) + pos, size))
      {
           return -EFAULT;
      }
      else
      {
           *off += size;
           ret = size;
           printk(KERN_INFO,"read size = %d frome %d\n", size, *off);
      }
   
      return ret;
   }
   
   static ssize_t write_kindlemem(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off)
   {
      int ret = 0;
      unsigned long pos = *off;
      unsigned long size = count;
      //获取文件结构体中私有数据
      struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;
      
      if (pos >= KINDLEMEM_SIZE)
           return 0;
      if (size > KINDLEMEM_SIZE - pos)
           size = KINDLEMEM_SIZE - pos;
      if (copy_from_user(kindlemem_tmp->mem + pos, userbuf, size))
      {
           return -EFAULT;
      }
      else
      {
           *off += size;
           ret = size;
           printk(KERN_INFO,"read size = %d frome %d\n", size, *off);
      }
   }
   
   static long ioctl_kindlemem (struct file *file, unsigned int cmd, unsigned long arg)
   {
      //获取文件结构体中私有数据
      struct kindlemem *kindlemem_tmp = (struct kindlemem *)file->private_data;
   
      switch (cmd)
      {
          case KINDLE_MEM_CLEAR:
               memset(kindlemem_tmp->mem, 0, KINDLEMEM_SIZE);
               printk(KERN_INFO, "clear mem\n");
               break;
          default:
               return -EINIT;
     }
   }
   
   static loff_t llseek_kindlemem (struct file *file, loff_t offset, int origin)
   {
       loff_t ret = 0;
       switch (origin)
       {
           case OFFSET_HEAD: //从开始移动
               if(offset < 0 || (unsigned int)offset > KINDLEMEM_SIZE)
               {
                   ret = -EINVAL;
                   break;
               }
               file->f_pos = (unsigned int)offset;
               ret = file->f_pos;
               break;
           case OFFSET_CURRENT:
               if((file->f_pos + offset) < 0  || (unsigned int)(file->f_pos + offset) > KINDLEMEM_SIZE))
               {
                   ret = -EINVAL;
                   break;
               }
               file->f_pos += (unsigned int)offset;
               ret = file->f_pos;
               break;
               
          default:
           ret = -EINVAL;
           break;
       }
       return ret;
   }
   
   static int open_kindlemem(struct inode *inode, sturct file *file)
   {
       struct kindlemem *kindlemem_tmp = container_of(inode->i_cdev, struct kindlemem, cdev);//通过结构体中cdev的位置,找的结构体的地址
       file->private_data = kindlemem_tmp;
       return 0;
   }
   
   
   int release_kindlemem (struct inode *inode, struct file *file)
   {
       return 0;
   }
   
   
    static struct file_operations kindlemem_fops = {
       .owner = THIS_MODULE,
       .read = read_kindlemem,
       .write = write_kindlemem,
       .llseek = llseek_kindlemem,
       .open = open_kindlemem,
       .release =release_kindlemem,
       .unlock_ioctl = ioctl_kindlemem,
    };
//新加开始
    static ssize_t read_kindlemem1(struct file *file, char __user *buf, size_t count, loff_t *off)
    {
        printk(KERN_INFO,"read_kindlemem1 read s\n");
    }
   
    static ssize_t write_kindlemem1(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off)
    {
        printk(KERN_INFO,"write_kindlemem write s\n");
    }

  static struct file_operations kindlemem_fops1 = {
       .owner = THIS_MODULE,
       .read = read_kindlemem1,
       .write =  write_kindlemem1,
       .open = open_kindlemem,
    };
//新加结束   
   
   //进行cdev的初始化配置
   #if 0
   //多个cdev共用一个fops
   void setup_kindle_cdev(struct kindlemem *dev, int minor)
   {
       int error;
       dev_t devno = MKDEV(major, minor);
       cdev_init(&dev->cdev, &kindlemem_fops);//初始化一个cdev,对内部成员ops进行赋值
       dev->cdev.owner = THIS_MODULE;
       error = cdev_add(&dev->cdev, devno, MEM_MINORCNT);//将初始化完成的cdev根据设备号devno添加到相应的位置,MEM_MINORCNT是共同使用cdev的此设备数
       if (error)
       {
           printk(KERN_NOTICE "ERROR %d %d", devno, minor);
       }
       memset(dev->mem, 0, KINDLEMEM_SIZE);
   }
   #else
   //修改,cdev使用不同的fops
   void setup_kindle_cdev(struct kindlemem *dev, int minor)
   {
       int error;
       dev_t devno = MKDEV(major, minor);
       switch(minor)
       {
        case 0:
            cdev_init(&dev->cdev, &kindlemem_fops);//初始化一个cdev,对内部成员ops进行赋值
            break;
        case 1:
             cdev_init(&dev->cdev, &kindlemem_fops1);//初始化一个cdev,对内部成员ops进行赋值
             break;
        default:
            cdev_init(&dev->cdev, &kindlemem_fops);//初始化一个cdev,对内部成员ops进行赋值
            break;
       }
       dev->cdev.owner = THIS_MODULE;
       error = cdev_add(&dev->cdev, devno, 1);//将初始化完成的cdev根据设备号devno添加到相应的位置
       if (error)
       {
           printk(KERN_NOTICE "ERROR %d %d", devno, minor);
       }
       memset(dev->mem, 0, KINDLEMEM_SIZE);
   }
   #endif
   
   static int __init init_kindlemem(void )
   {
       int ret, i;
       dev_t devno = MKDEV(major, 0);
   
       if(major)
       {
         //register_chrdev_region(dev_t from 从哪开始, unsigned count区域个数, const char * name)
         //给major下minor0-10创建一个cdev
         ret = register_chrdev_region(devno, MEM_MINORCNT, "kindlemem");
       }
       else
       {
           //baseminor:起始位置
          // alloc_chrdev_region(dev_t * dev, unsigned baseminor, unsigned count, const char * name) 
          ret = alloc_chrdev_region(&devno, 0, MEM_MINORCNT, "kindlemem");
          major = MAJOR(devno);//上面函数会堆major复制,这里用这个函数来提取
       }
       kindlemem_p = kzalloc(sizeof(struct kindlemem) * MEM_MINORCNT, GFP_KERNEL);
       if (!kindlemem_p)
       {
           ret = -1;
           goto fail_kzalloc;
       }
       //注册多个cdev实现多个ops,通过循环,并且minor修改成i
       for (i = 0; i < MEM_MINORCNT; i++)
       {
            setup_kindle_cdev(kindlemem_p[i], i);
       }
       if (ret < 0)
           goto fail;
       return ret;
       fail:
           printk(KERN_ERR "kindlmem: failed to register chrdev region\n");
           return ret;
       fail_kzalloc:
           printk(KERN_ERR "kindlmem: failed to alloc memory\n");
           unregister_chrdev_region(devno, MEM_MINORCNT);
           return ret;
   }
   
   
   static int __exit exit_kindlemem(void )
   {
       int i;
       //修改
       //注销多个cdev实现多个ops
       for (i = 0; i < MEM_MINORCNT; i++)
       {
            cdev_del(&(kindlemem_p[i]->cdev));
       }
       
       kfree(kindlemem_p);
       unregister_chrdev_region(devno, MEM_MINORCNT);
   }
   
   
   module_init(init_kindlemem);
   module_exit(exit_kindlemem);
   MODULE_LICENSE("GPL v2");
   MODULE_AUTHOR("David Howells <dak@qq.com>");



3 驱动中基本结构

3.1 module

#include <linux/module.h>
// #ifndef _ASM_ARM_MODULE_H
// #define _ASM_ARM_MODULE_H

// struct mod_arch_specific
// {
// 	int foo;
// };

// #define Elf_Shdr	Elf32_Shdr
// #define Elf_Sym		Elf32_Sym
// #define Elf_Ehdr	Elf32_Ehdr

// /*
//  * Include the ARM architecture version.
//  */
// #define MODULE_ARCH_VERMAGIC	"ARMv" __stringify(__LINUX_ARM_ARCH__) " "

// #endif /* _ASM_ARM_MODULE_H */

// 为了给insmod rmmod lsmod 指定函数int request_module(const char *fmt, ...)

static int __init InitChardrv_first(void)  //加上__init,放到.init_text段中 运行完后会这个段会被释放掉。不加的话,这个函数会放在其他位置,运行后也不释放
{
    return 1;
}

static int __exit ExitChardrv_first(void)//加上__exit, 放到.exit_text段中 运行完后会这个段会被释放掉。
{
    return 1;    
}

module_init(InitChardrv_first); //把函数映射到指定的表中, 在insmod命令时通过表找这个函数
module_exit(ExitChardrv_first); //把函数映射到指定的表中, 在rmmod命令时通过表找这个函数

modprobe 比 insmode高级,一次加载所有相关模块(有依赖关系的模块,依赖关系在modules.dep中)

makefie

这个makefile算是linux中makefile的子makefile。
只是利用linux中内核来编译自己的代码

obj-m表示以模块的形式进行编译,需要进行insmod等命令进行操作
还有 obi-y表示编译到内核 obj-n表示不做任何操作

KERN_DIR = /work/system/linux-2.6.22

all:
    make -C $(KERN_DIR) M = 'pwd' modules

clean:
    make -C $(KERN_DIR) M = 'pwd' modules clean
    rm -rf modules.order

obj-m += char_drv.o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值