字符驱动学习

    写完第一个hello world 的驱动以后,想编写一个比较完整的字符设备驱动,就把《linux 设备驱动开发详解》上的globalmem驱动写了一下,写完以后,发现了几个问题,没有自动生成设备节点,在多设备的支持中,两个设备的关系不明确。搜集了一下资料,编写了一个能够自动生成设备节点的字符驱动。
   在字符驱动中,内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。这三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个char_device_struct 结构)。

register_chrdev_region(dev_t first,unsigned int count,char *name) 
First :要分配的设备编号范围的初始值(次设备号常设为0); 
Count:连续编号范围. 
Name:编号相关联的设备名称. (/proc/devices); 

动态分配: 
Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name); 
Firstminor : 通常为0; 
*dev:存放返回的设备号; 

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;                          // 指向字符设备驱动程序描述符的指针
   } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

使用udev自动生成设备节点的函数:

class_create(…)在/drivers/base/class.c中实现:

  /**

  * class_create - create a struct class structure

  * @owner: pointer to the module that is to "own" this struct class

  * @nAME: pointer to a string for the name of this class.

  *

  * This is used to create a struct class pointer that can then be used

  * in calls to device_create().

  *

  * Note, the pointer created here is to be destroyed when finished by

  * making a call to class_destroy().

  */

struct class *class_create(struct module *owner, cONst char *name)

第一个参数指定类的所有者是哪个模块,第二个参数指定类名。

device_create(…)函数在/drivers/base/core.c中实现:
  /**
  * device_create - creates a device and registers it with sysfs
  * @class: pointer to the struct class that this device should be registered to
  * @parent: pointer to the parent struct device of this new device, if any
  * @devt: the dev_t for the char device to be added
  * @fmt: string for the device's name
  *
  * This funcTIon can be used by char device classes. A struct device
  * will be created in sysfs, registered to the specified class.
  *
  * A "dev" file will be created, showing the dev_t for the device, if
  * the dev_t is not 0,0.
  * If a pointer to a parent struct device is passed in, the newly created
  * struct device will be a child of that device in sysfs.
  * The pointer to the struct device will be returned from the call.
  * Any further sysfs files that might be required can be created using this
  * pointer.
  *
  * Note: the struct class passed to this function must have previously
  * been created with a call to class_create().
  */
struct device *device_create(struct class *class, struct device *parent,   dev_t devt, const char *fmt, ...)
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。

下面的代码就是支持两个设备节点,但是两个设备节点支持同一个设备。
static struct class *cdev_class;
static int __init gm_cdev_init(void)
{
    int result;
    dev_t devno = MKDEV(MAJOR_NR, 0);

    if(!= MAJOR_NR){
        result = register_chrdev_region(devno, 2, DEV_NAME);  //注册设备 支持两个设备,此设备号从0开始
    } else {
        result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
        MAJOR_NR = MAJOR(devno);
    }

    printk(KERN_CRIT"hello\n");
    if(result < 0)
        return result;

    gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
    if(NULL == gm_cdevp){
        unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
        result = -ENOMEM;
    }

    memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
    gm_setup_cdev(&gm_cdevp[0], 0);
    gm_setup_cdev(&gm_cdevp[0], 1);
    //gm_setup_cdev(&gm_cdevp[1], 1);                                        //使用这一行的话支持两个设备

    cdev_class = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(cdev_class))
        return PTR_ERR(cdev_class);

    device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0); //生成 /dev/gmem0 节点
    device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1)//生成 /dev/gmem1 节点    return 0;
}

void gm_cdev_exit(void)
{
    cdev_del(&gm_cdevp[0].cdev);                    
    cdev_del(&gm_cdevp[0].cdev);

    device_destroy(cdev_class, MKDEV(MAJOR_NR, 0));              //delete device node under /dev
    device_destroy(cdev_class, MKDEV(MAJOR_NR, 1));              //delete device node under /dev
    class_destroy(cdev_class);                                   //delete class created by us

    kfree(gm_cdevp);
    unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
}
root@wang:/work/wanghuan/drives# ls
cdev.c  cdev.ko  hello.c  Makefile  modules.order
root@wang:/work/wanghuan/drives# insmod cdev.ko 
root@wang:/work/wanghuan/drives# lsmod | grep cdev
cdev                    1853  0 
root@wang:/work/wanghuan/drives# ls /dev/gmem* -l
crw------- 1 root root 250, 0 2011-06-12 14:00 /dev/gmem0
crw------- 1 root root 250, 1 2011-06-12 14:00 /dev/gmem1
root@wang:/work/wanghuan/drives# ls > /dev/gmem0 
root@wang:/work/wanghuan/drives# cat /dev/gmem1
cdev.c
cdev.ko
hello.c
Makefile
modules.order


下面的代码就是支持两个设备节点,但是两个设备节点支持不同的设备。
static struct class *cdev_class;  static  int __init gm_cdev_init (void ) {
     int result ;
    dev_t devno  = MKDEV (MAJOR_NR , 0 ) ;

     if (! = MAJOR_NR ) {
        result  = register_chrdev_region (devno , 2 , DEV_NAME ) ;  //注册设备 支持两个设备,此设备号从0开始
     }  else  {
        result  = alloc_chrdev_region ( &devno , 0 , 2 , DEV_NAME ) ;
        MAJOR_NR  = MAJOR (devno ) ;
     }

    printk (KERN_CRIT "hello\n" ) ;
     if (result  < 0 )
        return result ;

    gm_cdevp  = kmalloc (2 *sizeof (struct gm_cdev_t ) , GFP_KERNEL ) ;
     if ( NULL  = = gm_cdevp ) {
        unregister_chrdev_region (MKDEV (MAJOR_NR , 0 ) , 2 ) ;
        result  =  -ENOMEM ;
     }

    memset (gm_cdevp , 0 , 2 *sizeof (struct gm_cdev_t ) ) ;
    gm_setup_cdev ( &gm_cdevp [0 ] , 0 ) ;
    //gm_setup_cdev ( &gm_cdevp [0 ] , 1 ) ;
    gm_setup_cdev ( &gm_cdevp [1 ] , 1 ) ;                                        //使用这一行的话支持两个设备

    cdev_class  = class_create (THIS_MODULE , DEV_NAME ) ;
     if (IS_ERR (cdev_class ) )
        return PTR_ERR (cdev_class ) ;

    device_create (cdev_class ,  NULL , MKDEV (MAJOR_NR , 0 ) ,     NULL , DEV_NAME "%d" , 0 ) ; //生成 /dev/gmem0 节点
    device_create (cdev_class ,  NULL , MKDEV (MAJOR_NR , 1 ) ,     NULL , DEV_NAME "%d" , 1 ) //生成 /dev/gmem1 节点    return 0 ;
}

void gm_cdev_exit (void )
{
    cdev_del ( &gm_cdevp [0 ] .cdev ) ;                    
    cdev_del ( & gm_cdevp [ 1 ] . cdev ) ;

    device_destroy (cdev_class , MKDEV (MAJOR_NR , 0 ) ) ;               / /delete device node under  /dev
    device_destroy (cdev_class , MKDEV (MAJOR_NR , 1 ) ) ;               / /delete device node under  /dev
    class_destroy (cdev_class ) ;                                    / /delete  class created by us

    kfree (gm_cdevp ) ;
    unregister_chrdev_region (MKDEV (MAJOR_NR , 0 ) , 2 ) ;
} root@wang:/work/wanghuan/drives# ls cdev.c  cdev.ko  hello.c  Makefile  modules.order root@wang:/work/wanghuan/drives# insmod cdev.ko  root@wang:/work/wanghuan/drives# lsmod | grep cdev cdev                    1853  0  root@wang:/work/wanghuan/drives# ls /dev/gmem* -l crw------- 1 root root 250, 0 2011-06-12 14:08 /dev/gmem0 crw------- 1 root root 250, 1 2011-06-12 14:08 /dev/gmem1 root@wang:/work/wanghuan/drives# ls > /dev/gmem0 root@wang:/work/wanghuan/drives# cat /dev/gmem1 root@wang:/work/wanghuan/drives# cat /dev/gmem0 cdev.c cdev.ko hello.c Makefile modules.order
  1. /**
  2.  * =====================================================================================
  3.  * Filename: cdev.c
  4.  * Description: 字符设备驱动模型
  5.  * Version: 1.0
  6.  * Created: 2011年06月02日 19时27分50秒
  7.  * Revision: none
  8.  * Compiler: gcc
  9.  *
  10.  * Author: wanghuan,
  11.  * Company:
  12.  * =====================================================================================
  13.  */

  14. #include <linux/module.h>
  15. #include <linux/types.h>
  16. #include <linux/fs.h>         /* file_operation */
  17. #include <linux/errno.h>     /* Error number */
  18. #include <linux/mm.h>    
  19. #include <linux/sched.h>    
  20. #include <linux/slab.h>    
  21. #include <linux/init.h>     /* __init __exit */
  22. #include <linux/device.h>
  23. #include <linux/cdev.h>    
  24. #include <asm/io.h>    
  25. #include <asm/system.h>    
  26. #include <asm/uaccess.h>     /* copy_to_user, copy_from_user */
  27. #include <linux/kernel.h>     /* printk() */

  28. #define GLOBALMEM_SIZE 0x1000
  29. #define MEM_CLEAN 0x1
  30. #define DEV_NAME "gmem"
  31. #define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)

  32. static int MAJOR_NR = 250;        /* Driver Major Number */
  33. //static int MINOR_NR = 0;        /* Driver Major Number */

  34. struct gm_cdev_t{
  35.     struct cdev cdev;
  36.     unsigned char mem[GLOBALMEM_SIZE];
  37. };
  38. static struct gm_cdev_t *gm_cdevp;

  39. static int gm_open(struct inode *inode, struct file *filp)
  40. {
  41.     struct gm_cdev_t *dev;
  42.     dev = container_of(inode->i_cdev, struct gm_cdev_t, cdev);
  43.     filp->private_data = dev;
  44.     return 0;
  45. }
  46. static int gm_release(struct inode *inode, struct file *filp)
  47. {
  48.     filp->private_data = NULL;
  49.     return 0;
  50. }
  51. static ssize_t gm_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  52. {
  53.     unsigned long p = *fpos;
  54.     int ret = 0;
  55.     
  56.     struct gm_cdev_t *dev = filp->private_data;
  57.     if(p >= GLOBALMEM_SIZE)
  58.         return 0;

  59.     if(count > GLOBALMEM_SIZE - p)
  60.         count = GLOBALMEM_SIZE -p;

  61.     if(copy_to_user(buf, (void*)(dev->mem) + p, count))
  62.         ret = -EFAULT;
  63.     else{
  64.         *fpos +=count;
  65.         ret =count;

  66.         printk(KERN_INFO "read %d butes from %ld \n", count, p);
  67.     }

  68.     return ret;
  69. }

  70. static ssize_t gm_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  71. {
  72.     unsigned long p = *fpos;
  73.     int ret = 0;

  74.     struct gm_cdev_t *dev = filp->private_data;
  75.     if(p >= GLOBALMEM_SIZE)
  76.      return 0;
  77.     
  78.     if(p >= GLOBALMEM_SIZE - p)
  79.         count = GLOBALMEM_SIZE - p;

  80.     if(copy_from_user(dev->mem + p, buf, count))
  81.         ret = - EFAULT;
  82.     else{
  83.         *fpos +=count;
  84.         ret = count;

  85.         printk(KERN_INFO "write %d butes from %ld \n", count, p);
  86.     }

  87.     return ret;
  88. }

  89. static loff_t gm_llseek(struct file *filp, loff_t offset, int orig)
  90. {
  91.     loff_t ret;
  92.     switch(orig){
  93.         case 0:
  94.             if(offset < 0){
  95.                 ret = -EFAULT;
  96.                 break;
  97.             }
  98.             if((unsigned int)offset > GLOBALMEM_SIZE){
  99.                 ret = -EFAULT;
  100.                 break;
  101.             }

  102.             filp->f_pos = (unsigned int)offset;
  103.             ret = filp->f_pos;
  104.             break;
  105.         case 1:
  106.             if(filp->f_pos + offset > GLOBALMEM_SIZE){
  107.                 ret = -EFAULT;
  108.                 break;
  109.             }

  110.             if(filp->f_pos + offset < 0){
  111.                 ret = -EFAULT;
  112.                 break;
  113.             }

  114.             filp->f_pos += offset;
  115.             ret = filp->f_pos;
  116.             break;
  117.         default:
  118.             ret = -EFAULT;
  119.     }
  120.     return ret;
  121. }

  122. static int gm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  123. {
  124.     struct gm_cdev_t *dev = filp->private_data;
  125.     switch(cmd){
  126.         case MEM_CLEAR:
  127.             memset(dev->mem, 0, GLOBALMEM_SIZE);
  128.             printk(KERN_INFO "globalmem is set to zero \n");
  129.             break;
  130.         default:
  131.             return -EINVAL;
  132.     }

  133.     return 0;
  134. }

  135. static const struct file_operations gm_fops = {
  136.     .owner = THIS_MODULE,
  137.     .open = gm_open,
  138.     .release = gm_release,
  139.     .read = gm_read,
  140.     .write = gm_write,
  141.     .ioctl = gm_ioctl,
  142.     .llseek = gm_llseek,
  143. };

  144. static void gm_setup_cdev(struct gm_cdev_t *gm_cdev, int minor)
  145. {
  146.     int err;
  147.     dev_t devno = MKDEV(MAJOR_NR, minor);
  148.     
  149.     cdev_init(&gm_cdev->cdev, &gm_fops);
  150.     gm_cdevp->cdev.owner = THIS_MODULE;
  151.     err = cdev_add(&gm_cdev->cdev, devno, 1);
  152.     if(err != 0)
  153.         printk(KERN_NOTICE "Error %d adding gmen", err);
  154. }

  155. static struct class *cdev_class;
  156. static int __init gm_cdev_init(void)
  157. {
  158.     int result;
  159.     dev_t devno = MKDEV(MAJOR_NR, 0);

  160.     if(0 != MAJOR_NR){
  161.         result = register_chrdev_region(devno, 2, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
  162.     } else {
  163.         result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
  164.         MAJOR_NR = MAJOR(devno);
  165.     }

  166.     printk(KERN_CRIT"hello\n");
  167.     if(result < 0)
  168.         return result;

  169.     gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
  170.     if(NULL == gm_cdevp){
  171.         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
  172.         result = -ENOMEM;
  173.     }

  174.     memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
  175.     gm_setup_cdev(&gm_cdevp[0], 0);
  176.     //gm_setup_cdev(&gm_cdevp[0], 1);
  177.     gm_setup_cdev(&gm_cdevp[1], 1);

  178.     cdev_class = class_create(THIS_MODULE, DEV_NAME);
  179.     if(IS_ERR(cdev_class))
  180.         return PTR_ERR(cdev_class);

  181.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0);
  182.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1);
  183.     return 0;
  184. }

  185. void gm_cdev_exit(void)
  186. {
  187.     cdev_del(&gm_cdevp[0].cdev);
  188.     cdev_del(&gm_cdevp[1].cdev);

  189.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
  190.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
  191.     class_destroy(cdev_class); //delete class created by us

  192.     kfree(gm_cdevp);
  193.     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2); //由于注册了两个设备,最后一个参数为 2
  194. }

  195. module_init(gm_cdev_init);
  196. module_exit(gm_cdev_exit);
  197. MODULE_LICENSE("GPL");
  198. MODULE_AUTHOR("wanghuan");

阅读(379) | 评论(0) | 转发(1) |
0

上一篇:ubuntu 内核树

下一篇:RT3070 STA 驱动移植

相关热门文章
  • SHTML是什么_SSI有什么用...
  • 卡尔曼滤波的原理说明...
  • shell中字符串操作
  • 关于java中的“错误:找不到或...
  • linux设备驱动归纳总结...
  • linux dhcp peizhi roc
  • 关于Unix文件的软链接
  • 求教这个命令什么意思,我是新...
  • sed -e "/grep/d" 是什么意思...
  • 谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议

你可能感兴趣的:(嵌入式)