字符设备驱动1

来源:互联网 发布:python reduce 函数 编辑:程序博客网 时间:2024/06/01 17:28

装载自:点击打开链接


1.cdev 结构体

Linux2.6内核使用 cdev 结构体描述字符设备

struct cdev // cdev 的定义在 <linux/cdev.h>

{

    struct kobject kobj;   //内嵌的kobject

    struct module *owner;   //所属模块

    const struct file_operations *ops;  

        //文件操作结构,在写驱动时,其结构体内的大部分函数要被实现

    struct list_head list;

    dev_t dev;          //设备号,int 类型,高12位为主设备号,低20位为次设备号

    unsigned int count;

};

其中 dev_t 定义了设备号为32位,前12位为主设备号,后20位为次设备号.可以使用如下宏调用来获得主、次设备号:

MAJOR(dev_t dev)

MINOR(dev_t dev)

MKDEV(int major,int minor) //通过主次设备号来生成dev_t

以上宏调用在内核源码中如此定义:

#define MINORBITS       20

#define MINORMASK       ((1U << MINORBITS) - 1)

//(1<<20 -1) 此操作后,MINORMASK宏的低20位为1,高12位为0

#define MAJOR(dev)      ((unsigned int) ((dev) >> MINORBITS))

#define MINOR(dev)      ((unsigned int) ((dev) & MINORMASK))

#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

 

下面一组函数用来对cdev结构体进行操作:

void cdev_init(struct cdev *, const struct file_operations *);//初始化,建立cdev和file_operation 之间的连接

struct cdev *cdev_alloc(void); //动态申请一个cdev内存

void cdev_put(struct cdev *p); //释放

int cdev_add(struct cdev *, dev_t, unsigned); //注册设备,通常发生在驱动模块的加载函数中

void cdev_del(struct cdev *);//注销设备,通常发生在驱动模块的卸载函数中

cdev_init()的源代码清单如下

// linux/fs/char_dev.c

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

  memset(cdev, 0, sizeof *cdev);

  INIT_LIST_HEAD(&cdev->list);

  kobject_init(&cdev->kobj, &ktype_cdev_default);

  cdev->ops = fops;

}

cdev_alloc()用于动态申请一个cdev内存,代码清单如下

struct cdev *cdev_alloc(void)

{

         struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);

         if (p) {

                   INIT_LIST_HEAD(&p->list);

                   kobject_init(&p->kobj, &ktype_cdev_dynamic);

         }

         return p;

}

cdev_add 和 cdev_del 分别用于添加和删除一个 cdev,完成字符设备的注册和注销。

在字符设备模块加载函数中调用 cdev_add,模块写在函数中调用 cdev_del

 

 

2. 分配和释放设备号

在注册时应该先调用:

int register_chrdev_region(dev_t from,unsigned count,const char *name)函数为其分配设备号,

此函数可用:

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)函数代替,

他们之间的区别在于:register_chrdev_region()用于已知设备号时,另一个用于动态申请,其优点在于不会造成设备号重复的冲突。

在注销之后,应调用:void unregister_chrdev_region(dev_t from,unsigned count)函数释放原先申请的设备号。

他们之间的顺序关系如下:

register_chrdev_region()-->cdev_add() //加载模块中

cdev_del()-->unregister_chrdev_region() //卸载模块中

3. file_operations 结构体

主要成员有open, close , llseek, read, write, readdir, ioctl, mmap,poll等,结构比较庞大,他们最终会被调用

 

4 Linux字符设备驱动的组成

4.1 字符设备驱动模块加载与卸载

模板如下

//xxdevice struct

struct xxx_dev

{

  struct cdev cdev;

   …

}xxx_dev;

static int __init xxx_init(void)

{

  …

  cdev_init( &xxx_dev.cdev, &xx_fops);//init the cdev ,and add file operations

  register_chrdev_region  or  alloc_chrdev_region  //get the device number

  ret = cdev_add( )// register the device

  …

}

static void __exit xxx_exit(void)

{

  …

  Unrgister the device number use unregister_chrdev_region()

  …

  cdev_del()

  …

}

 

4.2 完成file_operations中的成员函数

    按照file_operations中定义的统一结构编写实现函数

    注意 copy_from_user 和 copy_to­_user 的使用

struct file_operations xxx_fops =//init the fops

{

  .owner = THIS_MODULE,

  .read = xxx_read,

  .open = xxx_open,

  …

};

 

具体实例  :

 

[cpp] view plaincopy
  1. /* 
  2. ch6 最简单的字符设备驱动 
  3.     未加并发控制 
  4. */  
  5.   
  6. #include <linux/module.h>  
  7. #include <linux/types.h>  
  8. #include <linux/fs.h>  
  9. #include <linux/errno.h>  
  10. #include <linux/mm.h>  
  11. #include <linux/sched.h>  
  12. #include <linux/init.h>  
  13. #include <linux/cdev.h>  
  14. #include <asm/io.h>  
  15. #include <asm/system.h>  
  16. #include <asm/uaccess.h>  
  17.   
  18. #define GLOBALMEM_SIZE 0x1000  
  19. #define MEM_CLEAR 0x1  
  20. #define GLOBALMEM_MAJOR 260  
  21.   
  22. static int globalmem_major = GLOBALMEM_MAJOR;  
  23. struct globalmem_dev  
  24. {  
  25.     struct cdev cdev;  
  26.     unsigned char mem[GLOBALMEM_SIZE];  
  27. };   
  28.   
  29. struct globalmem_dev *globalmem_devp;  
  30.   
  31. int globalmem_open(struct inode *inode, struct file *filp)  
  32. {  
  33.     filp->private_data = globalmem_devp;  
  34.     return 0;  
  35. }  
  36.   
  37. int globalmem_release(struct inode *inode, struct file *filp)  
  38. {  
  39.     return 0;  
  40. }  
  41.   
  42. static int globalmem_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
  43. {  
  44.     struct globalmem_dev *dev = filp->private_data;  
  45.     switch( cmd )  
  46.     {  
  47.       case MEM_CLEAR:  
  48.         memset(dev->mem, 0, GLOBALMEM_SIZE);  
  49.         printk(KERN_INFO"globalmem is set to zero/n");  
  50.         break;  
  51.       default:  
  52.         return -EINVAL;  
  53.     }  
  54.     return 0;  
  55. }  
  56.   
  57. static int globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)  
  58. {  
  59.     unsigned long p = *ppos;  
  60.     unsigned int count = size;  
  61.     int ret = 0;  
  62.     struct globalmem_dev *dev = filp->private_data;  
  63.       
  64.     if(p > GLOBALMEM_SIZE)  
  65.       return count ? -ENXIO: 0;  
  66.     if(count > GLOBALMEM_SIZE - p)  
  67.       return GLOBALMEM_SIZE - p;  
  68.       
  69.     if(copy_to_user(buf, (void*)(dev->mem + p), count))  
  70.     {  
  71.       ret = - EFAULT;  
  72.     }  
  73.     else  
  74.     {  
  75.       *ppos += count;  
  76.       ret = count;   
  77.       printk(KERN_INFO"read %d bytes(s) from %d/n", count, p);  
  78.     }  
  79.     return ret;  
  80. }  
  81.   
  82. static int globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)  
  83. {  
  84.     unsigned long p = *ppos;  
  85.     unsigned int count = size;  
  86.     int ret = 0;  
  87.     struct globalmem_dev *dev = filp->private_data;  
  88.       
  89.     if(p > GLOBALMEM_SIZE)  
  90.       return count ? -ENXIO: 0;  
  91.     if(count > GLOBALMEM_SIZE - p)  
  92.       return GLOBALMEM_SIZE - p;  
  93.       
  94.     if(copy_from_user(dev->mem + p, buf, count))  
  95.       ret = - EFAULT;  
  96.     else  
  97.     {  
  98.       *ppos += count;  
  99.       ret = count;   
  100.       printk(KERN_INFO"write %d bytes(s) from %d/n", count, p);  
  101.     }  
  102.   
  103.     return ret;  
  104. }  
  105.   
  106. static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)  
  107. {  
  108.     loff_t ret = 0;  
  109.     switch(orig)  
  110.     {  
  111.       case 0:  
  112.         if( offset < 0)  
  113.         {  
  114.       ret = - EINVAL;  
  115.       break;  
  116.     }  
  117.     if((unsigned int)offset > GLOBALMEM_SIZE)  
  118.     {  
  119.       ret = - EINVAL;  
  120.       break;  
  121.     }  
  122.     filp->f_pos = (unsigned int)offset;  
  123.     ret = filp->f_pos;  
  124.     break;  
  125.       case 1:  
  126.         if((filp->f_pos + offset) > GLOBALMEM_SIZE)  
  127.         {  
  128.       ret = - EINVAL;  
  129.       break;  
  130.     }  
  131.         if((filp->f_pos + offset) < 0)  
  132.     {  
  133.       ret = - EINVAL;  
  134.       break;  
  135.     }  
  136.     filp->f_pos += offset;  
  137.     ret = filp->f_pos;  
  138.     break;  
  139.       default:  
  140.         ret = - EINVAL;  
  141.         break;  
  142.     }  
  143.     return ret;  
  144. }  
  145.   
  146. static const struct file_operations globalmem_fops =  
  147. {  
  148.   .owner = THIS_MODULE,  
  149.   .llseek = globalmem_llseek,  
  150.   .read = globalmem_read,  
  151.   .write = globalmem_write,  
  152.   .ioctl = globalmem_ioctl,  
  153.   .open = globalmem_open,  
  154.   .release = globalmem_release,  
  155. };  
  156.   
  157. static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)  
  158. {  
  159.   int err, devno = MKDEV(globalmem_major, index);  
  160.     
  161.   cdev_init(&dev->cdev, &globalmem_fops);  
  162.   dev->cdev.owner = THIS_MODULE;  
  163.   dev->cdev.ops = &globalmem_fops;  
  164.   err = cdev_add(&dev->cdev, devno, 1);  
  165.   if(err)  
  166.     printk(KERN_NOTICE"Error %d adding LED%d", err, index);  
  167. }  
  168.   
  169. int __init globalmem_init(void)  
  170. {  
  171.   int result;  
  172.   dev_t devno = MKDEV(globalmem_major, 0);  
  173.   
  174.   if(globalmem_major)  
  175.     result = register_chrdev_region(devno, 1, "globalmem");  
  176.   else  
  177.   {  
  178.     result = alloc_chrdev_region(&devno, 0, 1, "globalmem");  
  179.     globalmem_major = MAJOR(devno);  
  180.   }  
  181.   
  182.   if(result < 0)  
  183.     return result;  
  184.   
  185.   globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);  
  186.   if(!globalmem_devp)  
  187.   {  
  188.     result = -ENOMEM;  
  189.     goto fail_malloc;  
  190.   }  
  191.   memset(globalmem_devp, 0, sizeof(struct globalmem_dev));  
  192.     
  193.   globalmem_setup_cdev(globalmem_devp, 0);  
  194.   return 0;  
  195. fail_malloc: unregister_chrdev_region(devno, 1);  
  196.   return result;  
  197. }  
  198.   
  199. void __exit globalmem_exit(void)  
  200. {  
  201.   cdev_del(&globalmem_devp->cdev);//logout the cdev  
  202.   kfree(globalmem_devp);  
  203.   unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);//release the device number  
  204. }  
  205.   
  206. MODULE_AUTHOR("tiany");  
  207. MODULE_LICENSE("Dual BSD/GPL");  
  208.   
  209. module_param(globalmem_major, int, S_IRUGO);  
  210.   
  211. module_init(globalmem_init);  
  212. module_exit(globalmem_exit);  

Makefile:

[cpp] view plaincopy
  1. obj-m := globalmem.o  
  2. KERNELDIR := /lib/modules/2.6.25-14.fc9.i686/build  
  3.   
  4. PWD := $(shell pwd)  
  5.   
  6. modules:  
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  8.   
  9. modules_install:  
  10.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  
  11.   
  12. clean:  
  13.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions  

运行 insmod globalmem.ko ,加载模块

lsmod 和 /proc/devices 中可以看见 globalmem

通过: mknod /dev/globalmem c $device_number 0 创建设备节点

在 /sysy/module 中会有相应的目录和文件


0 0
原创粉丝点击