Chinaunix首页 | 论坛 | 博客
  • 博客访问: 98505
  • 博文数量: 24
  • 博客积分: 407
  • 博客等级: 一等列兵
  • 技术积分: 291
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-02 08:31
文章分类
文章存档

2012年(24)

我的朋友

分类: LINUX

2012-02-27 18:41:49


前段时间转载了一篇“字符设备模型浅析”,此乃大神之作(在“好文推荐”目录中),膜拜许久,感觉不够,所以转载了。做个基层官员混口饭吃没关系,如果想进入核心领导层,想攀升,就必须理解背后的机制,背后十年功呀。希望想深入研究设备驱动注册机理的同志们分析下。我在这里之分析下几个注册函数,写个例子应用下,跟前者相比太献丑了,希望坚持带来提高,呵呵。
 
我先简单介绍下字符设备驱动的概念。
1、字符设备可以分为字符设备, 块设备,网络设备。
 字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。块设备可以用任意顺序进行访问,以块为单位进行操作,如硬盘、软驱等。字符设备不经过系统的快速缓冲,而块设备经过系统的快速缓冲。但是,字符设备和块设备并没有明显的界限,如对于Flash设备,符合块设备的特点,但是我们仍然可以把它作为一个字符设备来访问。
 
2、前面有一篇我讲到了应用程序如何最终调用设备驱动程序。现在我们看看字符设备驱动是如何注册的,如果想搞清楚这个问题,我还是建议看我转载的那篇文章。
 
(1)分配一个设备号,如果自己不知道哪些设备号空闲,可调用动态分配函数。
静态分配函数:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
使用:指定从设备号from开始,申请count个设备号,在/proc/devices中的名字为name。返回值:成功返回0,失败返回错误码。
动态分配函数:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
使用:动态申请从次设备号baseminor开始的count个设备号,在/proc/devices中的名字为name,并通过dev指针把分配到的设备号返回给调用函数者。返回值:成功返回0,失败返回错误码。
(2)注册设备。
首先分配一个cdev结构体
 
然后调用:void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数:cdev:之前我定义的cdev结构体;fops:设备对应的文件操作结构体。
返回值:(函数有可能失败,查看返回值是必须的)成功返回0,示范返回对应的错误码
这个函数将cdev与file_operations联系起来,我们还要手动指定test_cdev->owner = THIS_MODULE.这样内核能帮助我们维护cdev结构。
  1. void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  2.  529{
  3.  530 memset(cdev, 0, sizeof *cdev);
  4.  531 INIT_LIST_HEAD(&cdev->list);
  5.  532 cdev->kobj.ktype = &ktype_cdev_default;
  6.  533 kobject_init(&cdev->kobj);
  7.  534 cdev->ops = fops;
  8.  535}
 
然后添加设备到内核:int cdev_add(struct cdev *cdev, dev_t dev, unsigned count)
这个函数非常可能失败,所以我们要做相应的处理。有兴趣的可以看下cdev_add的源码。
 
(3)当设备驱动程序不用时,需卸载:注销设备所使用的设备号,void unregister_chrdev_region(dev_t from, unsigned count)注销cdev结构体void cdev_del(struct cdev *p)
 
3 我觉得有必要介绍下老的设备注册方法。
  1. int register_chrdev(unsigned int major, const char *name,
  2.  267 const struct file_operations *fops)
  3.  268{
  4.  269 struct char_device_struct *cd;
  5.  270 struct cdev *cdev;
  6.  271 char *s;
  7.  272 int err = -ENOMEM;
  8.  273
  9.  274 cd = __register_chrdev_region(major, 0, 256, name);
  10.  275 if (IS_ERR(cd))
  11.  276 return PTR_ERR(cd);
  12.  277
  13.  278 cdev = cdev_alloc();
  14.  279 if (!cdev)
  15.  280 goto out2;
  16.  281
  17.  282 cdev->owner = fops->owner;
  18.  283 cdev->ops = fops;
  19.  284 kobject_set_name(&cdev->kobj, "%s", name);
  20.  285 for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
  21.  286 *s = '!';
  22.  287
  23.  288 err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
  24.  289 if (err)
  25.  290 goto out;
  26.  291
  27.  292 cd->cdev = cdev;
  28.  293
  29.  294 return major ? 0 : cd->major;
  30.  295out:
  31.  296 kobject_put(&cdev->kobj);
  32.  297out2:
  33.  298 kfree(__unregister_chrdev_region(cd->major, 0, 256));
  34.  299 return err;
  35.  300}
仔细看下代码会发现,原来老的注册方法是我们讲的新方法的封装,它虽然在某种意义上带来了便利,但是也存在很多麻烦。对它的调用将为给定的主设备号注册0-255作为此设备号。使用这一接口必须能够处理256个此设备号的open调用,而且也不能使用大于255的测设备号。新的内核不主张使用这一接口。
 
下面参考书上写的一个简单的驱动程序,在系统上测试过。
 
  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/cdev.h>
  4. #include <linux/fs.h>
  5. #include <linux/types.h>
  6. #include <linux/slab.h>
  7. #include <linux/uaccess.h>
  8. #include <linux/moduleparam.h>
  9. //#include <linux/>


  10. MODULE_LICENSE("Dual BSD/GPL");

  11. int major=0;
  12. module_param(major,int,0);
  13. int minor=0;
  14. module_param(minor,int,0);
  15. struct test_dev{
  16.     char test[256];
  17.     struct cdev cdev;
  18. };
  19. struct test_dev* my_dev;

  20. int test_open(struct inode *inode,struct file *filp)
  21. {
  22.     printk(KERN_ALERT "open, tamclub");
  23.     return 0;
  24. }

  25. int test_release(struct inode *inode,struct file *filp)
  26. {
  27.     printk(KERN_ALERT "close, tamclub");
  28.     return 0;
  29. }

  30. static struct file_operations test_fops={
  31.     .owner=THIS_MODULE,
  32.     .open=test_open,
  33.     .release=test_release,
  34. };
  35. static int hello_init(void)
  36. {
  37.     int ret;
  38.     dev_t dev;
  39.     my_dev=kmalloc(sizeof(struct test_dev),GFP_KERNEL);
  40.     if(!my_dev){
  41.         printk("bad kmalloc");
  42.         return -ENOMEM;
  43.     }
  44.     if(major){
  45.         dev=MKDEV(major,minor);
  46.         ret=register_chrdev_region(dev,0,"test");    
  47.     }else{
  48.         ret=alloc_chrdev_region(&dev,minor,1,"test");
  49.         major=MAJOR(dev);
  50.         printk(KERN_ALERT "%d",major);
  51.     }
  52.     if(ret<0){
  53.         printk(KERN_ALERT "can't get major %d\n",major);
  54.     }
  55.     cdev_init(&my_dev->cdev,&test_fops);
  56.     my_dev->cdev.owner=THIS_MODULE;
  57.     my_dev->cdev.ops=&test_fops;
  58.     ret=cdev_add(&my_dev->cdev,dev,1);
  59.     if(ret){
  60.         printk(KERN_ALERT "error %d",ret);
  61.     }
  62.     
  63.     printk(KERN_ALERT "Hello, tmaclub\n");
  64.     return 0;
  65. }

  66. static void hello_exit(void)
  67. {
  68.     unregister_chrdev_region(MKDEV(major,minor),1);
  69.     cdev_del(&my_dev->cdev);
  70.     kfree(my_dev);
  71.     printk(KERN_ALERT "Goodbye, tmaclub\n");
  72. }

  73. module_init(hello_init);
  74. module_exit(hello_exit);

下面是makefile:

 

  1. ifeq ($(KERNELRELEASE),)

  2.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  3.     PWD := $(shell pwd)

  4. modules:
  5.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

  6. modules_install:
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

  8. clean:
  9.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

  10. .PHONY: modules modules_install clean

  11. else
  12.     # called from kernel build system: just declare what our modules are
  13.     obj-m :=my_dev.o

  14. endif

调试过程:

首先执行make

然后执行sudo insmod my_dev.ko,执行dmesg观察信息:[  207.350969] 252<1>Hello, tmaclub

创建dev文件:mknod /dev/my_dev c 252 0(252是自动分配的主设备号,有printk打印出)

最后执行cat /dev/my_dev

完成后执行dmesg观察内核的打印信息:[  300.442444] open, tamclub<1>close, tamclub

调试完成。

 

阅读(2177) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~